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

4
4个回答

11
是的,我们在GA版本中从Reliable Collections中删除了IEnumerable。正如Allan T所提到的,尽管它们表示相同的基本数据结构,但可靠集合并不像常规.NET集合一样。你可能已经注意到的其中一个重大区别是所有操作都是异步的,这是由于锁定语义和复制以及磁盘读取的I/O。正是后者驱动了删除IEnumerable的决策,因为它严格是同步的,而我们不是。相反,我们现在使用IAsyncEnumerable,但目前尚不支持完整的LINQ扩展方法集。
我们正在努力开发异步LINQ扩展方法,但与此同时,有几种方法可以使用IAsyncEnumerable。
Eli Arbel在Gist中提供了异步扩展方法的桥接,以便与System.Interactive.Async以及Select、SelectMany和Where的异步实现一起使用。
或者,您可以将IAsyncEnumerable包装在常规的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 Arbel
这些异步操作符支持诸如交叉查询其他集合的场景。 - Eli Arbel
感谢您的澄清,Eli,我已经更新了我的答案。 - Vaclav Turecek
谢谢这个扩展!它非常方便/有用。我能够用这些扩展替换我的旧代码。它立即生效。 - alltej
感谢您解释删除LINQ支持的原因,我已经想到了这种情况。扩展和NuGet库看起来很有前途,我会尝试一下。如果核心库中有异步LINQ实现,那将是一个令人信服的功能。这方面有时间表吗? - RWilkinson

7

我不知道这是否是“最佳”方法,但我一直在使用以下方法:

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 作为我的字典键。如果你需要使用不同的类型,你就需要相应地调整代码。 - Nick Barrett
这几乎与我最终所做的完全相同,谢谢。 - RWilkinson

3
在编写本答案时,IAsyncEnumerable 是 dotnet 库的一部分,并且 C# 8.0 添加了语法糖来支持它。
问题是 ServiceFabric 使用自己定义的 IAsyncEnumerable,因此您无法将 dotnet 扩展方法和 C# 辅助程序应用于它。
我想到的一个解决方案是使用简单的包装器将 ServiceFabric 的 IAsyncEnumerable 转换为本机的 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);

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

就在2015年10月,微软的一位高级程序经理发布了LINQ可用于该集合(上述第二个链接)。看起来,在服务织物GA之前,使用LINQ扩展的能力已被删除。 - RWilkinson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接