Каков наилучший способ поиска в IReliableDictionary?

На основании этих сообщений:

Преобразовать IReliableDictionary в IList

Что такое наиболее оптимальный метод запроса надежной коллекции словарей

Я должен иметь возможность использовать Linq для запроса IReliableDictionary, но может показаться, что этот интерфейс больше не реализует IEnumerable, а расширения Linq недоступны. По крайней мере, в версии 5.0.0.0 сборки Microsoft.ServiceFabric.Data.Interfaces.

Если это правда, как лучше всего искать IReliableDictionary?


person RWilkinson    schedule 26.04.2016    source источник


Ответы (4)


Да, мы удалили IEnumerable из надежных коллекций в выпуске GA. Как упоминает Аллан Т., надежные коллекции на самом деле не такие, как обычные коллекции .NET, хотя они представляют одни и те же фундаментальные структуры данных. Одно из больших отличий, которое вы, вероятно, заметили, заключается в том, что все операции являются асинхронными из-за блокировки семантики и операций ввода-вывода для репликации и чтения с диска. Именно последняя часть привела к решению удалить IEnumerable, потому что он строго синхронен, а мы нет. Вместо этого мы теперь используем IAsyncEnumerable, который пока не поддерживает полный набор методов расширения LINQ.

Мы работаем над асинхронными методами расширения LINQ, но пока есть несколько способов работы с IAsyncEnumerable.

У Эли Арбеля есть асинхронные методы расширения в 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()));

        }
person Vaclav Turecek    schedule 27.04.2016
comment
Небольшое примечание: расширения предоставляют мост к библиотеке Ix-Async NuGet, которая включает полную асинхронную реализацию LINQ. Основная часть относится к асинхронным версиям операторов, из которых я реализовал только SelectAsync, SelectManyAsync и WhereAsync. - person Eli Arbel; 28.04.2016
comment
(Эти асинхронные операторы поддерживают такие сценарии, как перекрестный запрос к другим коллекциям) - person Eli Arbel; 28.04.2016
comment
Спасибо за разъяснение, Эли, я обновил свой ответ. - person Vaclav Turecek; 28.04.2016
comment
Спасибо за расширение! Это очень удобно/полезно. Я смог заменить свой старый код, используя это расширение. Это работает сразу. - person alltej; 29.04.2016
comment
Спасибо за объяснение причин удаления поддержки LINQ, я так и думал. Расширение и библиотека nuget выглядят многообещающе, и я попробую их. Было бы неплохо, если бы в основных библиотеках была реализация асинхронного LINQ, так как это было бы привлекательной функцией. Есть ли сроки для них? - person RWilkinson; 29.04.2016

Я не знаю, является ли это «лучшим» способом, но я использую следующее

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, имя коллекции, которую вы хотите запросить, и лямбда-функцию с вашей логикой запроса. Например:

var queryResult = await QueryReliableDictionary<string>(this.StateManager, "CustomerCollection", name => !string.IsNullOrWhiteSpace(name) && (name.IndexOf("fred", StringComparison.OrdinalIgnoreCase) >= 0));
person Nick Barrett    schedule 28.04.2016
comment
Я должен упомянуть, что я использовал Guid для своих ключей словаря. Если вам нужно использовать другой тип, вам нужно будет соответствующим образом скорректировать код. - person Nick Barrett; 28.04.2016
comment
Это почти идентично тому, что я в итоге сделал, спасибо. - person RWilkinson; 29.04.2016

На момент написания этого ответа 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);
person Vitaliy Ulantikov    schedule 17.05.2020

Насколько мне известно, надежные словари сервисной фабрики могут выглядеть как обычные словари .NET, но на самом деле это не одно и то же. Таким образом, некоторые методы могут не поддерживаться в надежном словаре или очереди SF.

person alltej    schedule 27.04.2016
comment
Совсем недавно, в октябре 2015 года, старший менеджер программы из MS сообщил, что LINQ доступен в этой коллекции (вторая ссылка выше). Похоже, что возможность использования расширений LINQ была удалена до GA of Service Fabric. - person RWilkinson; 27.04.2016