搜索IReliableDictionary的最佳方法是什么?

时间:2016-04-26 23:31:22

标签: azure-service-fabric

基于这些帖子:

Convert IReliableDictionary to IList

What is the most optimal method of querying a reliable dictionary collection

我应该能够使用Linq查询IReliableDictionary,但看起来这个接口不再实现IEnumerable,并且Linq扩展不可用。至少在Microsoft.ServiceFabric.Data.Interfaces程序集的5.0.0.0版中。

如果这是真的,那么搜索IReliableDictionary的最佳方法是什么?

4 个答案:

答案 0 :(得分:10)

是的,我们确实从GA版本中的Reliable Collections中删除了IEnumerable。正如Allan T所提到的,Reliable Collections与常规.NET集合并不完全相同,即使它们代表相同的基础数据结构。您可能注意到的一个重大差异是所有操作都是异步的,因为锁定语义和I / O用于复制和磁盘读取。后者推动了删除IEnumerable的决定,因为它是严格同步的,我们不是。相反,我们现在使用IAsyncEnumerable,但它不支持完整的LINQ扩展方法集。

我们正在研究异步LINQ扩展方法,但与此同时,有两种方法可以使用IAsyncEnumerable。

Eli Arbel has async extension methods on Gist提供了System.Interactive.Async的桥梁以及Select,SelectMany和Where的异步实现。

或者你可以wrap IAsyncEnumerable in a regular IEnumerable简单地用同步方法包装异步调用,这将再次为你提供完整的LINQ扩展方法。然后,您可以在常规LINQ查询中使用扩展方法:

        using (ITransaction tx = this.StateManager.CreateTransaction())
        {
            var x = from item in (await clusterDictionary.CreateEnumerableAsync(tx)).ToEnumerable()
                   where item.Value.Status == ClusterStatus.Ready
                   orderby item.Value.CreatedOn descending
                   select new ClusterView(
                       item.Key,
                       item.Value.AppCount,
                       item.Value.ServiceCount,
                       item.Value.Users.Count(),
                       this.config.MaximumUsersPerCluster,
                       this.config.MaximumClusterUptime - (DateTimeOffset.UtcNow - item.Value.CreatedOn.ToUniversalTime()));

        }

答案 1 :(得分:2)

我不知道它是否是最好的"方式,但我一直在使用以下

public static async Task<IList<KeyValuePair<Guid, T>>> QueryReliableDictionary<T>(IReliableStateManager stateManager, string collectionName, Func<T, bool> filter)
{
    var result = new List<KeyValuePair<Guid, T>>();

    IReliableDictionary<Guid, T> reliableDictionary =
        await stateManager.GetOrAddAsync<IReliableDictionary<Guid, T>>(collectionName);

    using (ITransaction tx = stateManager.CreateTransaction())
    {
        IAsyncEnumerable<KeyValuePair<Guid, T>> asyncEnumerable = await reliableDictionary.CreateEnumerableAsync(tx);
        using (IAsyncEnumerator<KeyValuePair<Guid, T>> asyncEnumerator = asyncEnumerable.GetAsyncEnumerator())
        {
            while (await asyncEnumerator.MoveNextAsync(CancellationToken.None))
            {
                if (filter(asyncEnumerator.Current.Value))
                    result.Add(asyncEnumerator.Current);
            }
        }
    }
    return result;
}

您可以通过传递StateManager,要查询的集合的名称以及带有查询逻辑的lambda函数来使用该方法。例如:

var queryResult = await QueryReliableDictionary<string>(this.StateManager, "CustomerCollection", name => !string.IsNullOrWhiteSpace(name) && (name.IndexOf("fred", StringComparison.OrdinalIgnoreCase) >= 0));

答案 2 :(得分:2)

在编写此答案时,IAsyncEnumerable是dotnet库的一部分,C#8.0添加了语法糖来支持它。

问题在于ServiceFabric使用了自己的IAsyncEnumerable定义,因此您不能对其应用dotnet扩展方法和C#助手。

我想出的一种解决方案是使用一个简单的包装程序将ServiceFabric的IAsyncEnumerable转换为本地的:

using System.Threading;
using System.Threading.Tasks;

using Generic = System.Collections.Generic;
using Fabric = Microsoft.ServiceFabric.Data;

public static class FabricAsyncEnumerableExtensions
{
    /// <summary>
    /// Converts ServiceFabric <see cref="Microsoft.ServiceFabric.Data.IAsyncEnumerable"/> to dotnet native <see cref="System.Collections.Generic.IAsyncEnumerable"/>.
    /// </summary>
    public static Generic.IAsyncEnumerable<T> ToGeneric<T>(this Fabric.IAsyncEnumerable<T> source) =>
        new AsyncEnumerableWrapper<T>(source);

    private class AsyncEnumerableWrapper<T> : Generic.IAsyncEnumerable<T>
    {
        private readonly Fabric.IAsyncEnumerable<T> _source;

        public AsyncEnumerableWrapper(Fabric.IAsyncEnumerable<T> source) => _source = source;

        public Generic.IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken _ = default)
        {
            Fabric.IAsyncEnumerator<T> enumerator = _source.GetAsyncEnumerator();
            return new AsyncEnumeratorWrapper<T>(enumerator);
        }
    }

    private class AsyncEnumeratorWrapper<T> : Generic.IAsyncEnumerator<T>
    {
        private readonly Fabric.IAsyncEnumerator<T> _source;

        public AsyncEnumeratorWrapper(Fabric.IAsyncEnumerator<T> source) => _source = source;

        public async ValueTask DisposeAsync() =>
            await Task.Run(_source.Dispose).ConfigureAwait(false);

        public async ValueTask<bool> MoveNextAsync() =>
            await _source.MoveNextAsync(default).ConfigureAwait(false);

        public T Current => _source.Current;
    }
}

使用它就像调用扩展方法一样简单,然后将其用作常规IAsyncEnumerable

Fabric.IAsyncEnumerable<KeyValuePair<Guid, Product>> asyncProducts = GetAsyncProducts();
return await asyncProducts.ToGeneric()
    .Select(p => p.Value)
    .ToListAsync()
    .ConfigureAwait(false);

答案 3 :(得分:0)

据我所知,服务结构可靠的字典可能看起来像常规的.NET字典,但它们并不完全相同。因此,SF可靠字典或队列中可能不支持某些方法。