【问题标题】:What would be the best way to search an IReliableDictionary?搜索 IReliableDictionary 的最佳方法是什么?
【发布时间】:2016-08-21 00:43:59
【问题描述】:

根据这些帖子:

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 的最佳方法是什么?

【问题讨论】:

    标签: azure-service-fabric


    【解决方案1】:

    据我所知,Service Fabric 可靠字典可能看起来像普通的 .NET 字典,但实际上并不相同。因此,SF 可靠字典或队列中可能不支持某些方法。

    【讨论】:

    • 就在 2015 年 10 月,一位来自 MS 的高级项目经理发布说 LINQ 可用于此集合(上面的第二个链接)。在服务结构的 GA 之前,使用 LINQ 扩展的能力似乎已被删除。
    【解决方案2】:

    是的,我们确实在 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()));
    
            }
    

    【讨论】:

    • 请注意 - 扩展提供了通往 Ix-Async NuGet 库的桥梁,其中包括 完整 异步 LINQ 实现。基础部分是指操作符的异步版本,我只实现了SelectAsyncSelectManyAsyncWhereAsync
    • (这些异步操作符支持交叉查询其他集合等场景)
    • 感谢您的澄清,Eli,我更新了我的答案。
    • 感谢您的扩展!它非常方便/有用。我能够使用这些扩展替换我的旧代码。立即生效。
    • 感谢您解释删除 LINQ 支持背后的原因,我认为是这种情况。扩展和 nuget 库看起来很有希望,我会试一试。如果核心库中有一个异步 LINQ 实现会很好,因为这将是一个引人注目的特性。这些有时间表吗?
    【解决方案3】:

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

    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));
    

    【讨论】:

    • 我应该提一下,我一直在使用 Guid 作为我的字典键。如果您需要使用不同的类型,则需要相应地调整代码。
    • 这与我最终所做的几乎相同,谢谢。
    【解决方案4】:

    在撰写此答案时,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);
    

    【讨论】:

      猜你喜欢
      • 2016-11-20
      • 1970-01-01
      • 2010-10-17
      • 2010-12-14
      • 1970-01-01
      • 2019-01-24
      • 1970-01-01
      • 1970-01-01
      • 2021-04-24
      相关资源
      最近更新 更多