Поделиться через


Разбиение на страницы с помощью Azure SDK для .NET

В этой статье вы узнаете, как использовать функции разбиения на страницы из пакета Azure SDK для .NET, чтобы удобно и эффективно работать с большими наборами данных. Разбиение на страницы — это разделение больших наборов данных по страницам, чтобы объекту-получателю было проще перебирать данные меньшего объема. Начиная с версии C# 8, вам доступно создание и использование асинхронных (async) потоков. Асинхронные потоки основаны на интерфейсе IAsyncEnumerable<T>. Пакет Azure SDK для .NET предоставляет реализацию IAsyncEnumerable<T> со своим классом AsyncPageable<T>.

Все примеры в этой статье используют следующие пакеты NuGet:

Актуальный каталог пакетов Azure SDK для .NET см. в статье Последние выпуски пакета Azure SDK.

Типы возвращаемых значений для разбиения на страницы

Экземпляры клиентов, создаваемые из Azure SDK для .NET, могут возвращать следующие страничные типы значений:

Тип Описание
Pageable<T> Коллекция получаемых значений в виде страниц
AsyncPageable<T> Коллекция асинхронно получаемых значений в виде страниц

Большинство примеров в этой статье являются асинхронными и используют варианты типа AsyncPageable<T>. Использование асинхронного программирования идеально подходит для операций, связанных с вводом-выводом. Использование асинхронных API из пакета Azure SDK для .NET идеально подходит для операций, связанных с вводом-выводом, так как эти операции представляют сетевые вызовы HTTP/S.

Перебор AsyncPageable с помощью await foreach

Для перебора AsyncPageable<T> с использованием синтаксиса await foreach рассмотрим следующий пример:

async Task IterateSecretsWithAwaitForeachAsync()
{
    AsyncPageable<SecretProperties> allSecrets = client.GetPropertiesOfSecretsAsync();

    await foreach (SecretProperties secret in allSecrets)
    {
        Console.WriteLine($"IterateSecretsWithAwaitForeachAsync: {secret.Name}");
    }
}

В приведенном выше коде C#:

  • Вызывается метод SecretClient.GetPropertiesOfSecretsAsync, и возвращается объект AsyncPageable<SecretProperties>.
  • В цикле await foreach происходит асинхронная передача каждого значения SecretProperties.
  • При возникновении каждого secret в консоль записывается его имя (Name).

Перебор AsyncPageable с помощью while

Для перебора по AsyncPageable<T>, когда синтаксис await foreach недоступен, используйте цикл while.

async Task IterateSecretsWithWhileLoopAsync()
{
    AsyncPageable<SecretProperties> allSecrets = client.GetPropertiesOfSecretsAsync();

    IAsyncEnumerator<SecretProperties> enumerator = allSecrets.GetAsyncEnumerator();
    try
    {
        while (await enumerator.MoveNextAsync())
        {
            SecretProperties secret = enumerator.Current;
            Console.WriteLine($"IterateSecretsWithWhileLoopAsync: {secret.Name}");
        }
    }
    finally
    {
        await enumerator.DisposeAsync();
    }
}

В приведенном выше коде C#:

Перебор страниц AsyncPageable

Чтобы контролировать получение страниц значений от службы, используйте метод AsyncPageable<T>.AsPages:

async Task IterateSecretsAsPagesAsync()
{
    AsyncPageable<SecretProperties> allSecrets = client.GetPropertiesOfSecretsAsync();

    await foreach (Page<SecretProperties> page in allSecrets.AsPages())
    {
        foreach (SecretProperties secret in page.Values)
        {
            Console.WriteLine($"IterateSecretsAsPagesAsync: {secret.Name}");
        }

        // The continuation token that can be used in AsPages call to resume enumeration
        Console.WriteLine(page.ContinuationToken);
    }
}

В приведенном выше коде C#:

  • Вызывается метод SecretClient.GetPropertiesOfSecretsAsync, и возвращается объект AsyncPageable<SecretProperties>.
  • Вызывается метод AsyncPageable<T>.AsPages, и возвращается IAsyncEnumerable<Page<SecretProperties>>.
  • Происходит асинхронный перебор каждой страницы с использованием await foreach.
  • Каждая страница имеет набор значений Page<T>.Values, представляющих список IReadOnlyList<T>, который перебирается синхронно с помощью foreach.
  • Каждая страница также содержит маркер Page<T>.ContinuationToken, который можно использовать для запроса следующей страницы.

Использование System.Linq.Async с AsyncPageable

Пакет System.Linq.Async содержит набор методов LINQ, работающих с типом IAsyncEnumerable<T>. Поскольку AsyncPageable<T> реализует IAsyncEnumerable<T>, можно использовать System.Linq.Async для запроса и преобразования данных.

Преобразование в List<T>

Используйте ToListAsync для преобразования AsyncPageable<T> в List<T>. Этот метод может выполнить несколько вызовов службы, если данные не возвращаются на одной странице.

async Task ToListAsync()
{
    AsyncPageable<SecretProperties> allSecrets =
        client.GetPropertiesOfSecretsAsync();

    List<SecretProperties> secretList = await allSecrets.ToListAsync();

    secretList.ForEach(secret =>
        Console.WriteLine($"ToListAsync: {secret.Name}"));
}

В приведенном выше коде C#:

  • Вызывается метод SecretClient.GetPropertiesOfSecretsAsync, и возвращается объект AsyncPageable<SecretProperties>.
  • Ожидается метод ToListAsync, который выдает новый экземпляр List<SecretProperties>.

Взятие первых N элементов

Вы можете использовать Take для получения только первых N элементов AsyncPageable. Применение Take позволяет минимизировать число необходимых вызовов службы для получения N элементов.

async Task TakeAsync(int count = 30)
{
    AsyncPageable<SecretProperties> allSecrets =
        client.GetPropertiesOfSecretsAsync();

    await foreach (SecretProperties secret in allSecrets.Take(count))
    {
        Console.WriteLine($"TakeAsync: {secret.Name}");
    }
}

Другие методы

System.Linq.Async включает другие полезные методы, функционально эквивалентные своим синхронным аналогам Enumerable. Примеры таких методов: Select, Where, OrderBy и GroupBy.

Осторожно с вычислением на стороне клиента

При использовании пакета System.Linq.Async учитывайте, что операции LINQ выполняются на клиенте. Следующий запрос получает все элементы просто для их подсчета:

// ⚠️ DON'T DO THIS! 😲
int expensiveSecretCount =
    await client.GetPropertiesOfSecretsAsync()
        .CountAsync();

Предупреждение

То же предупреждение относится к таким операторам, как Where. Всегда старайтесь использовать фильтрацию, агрегацию и проекцию данных на стороне сервера, если они доступны.

Использование в качестве наблюдаемой последовательности

Пакет System.Linq.Async используется в основном для функций, работающих по шаблону наблюдения над последовательностями IAsyncEnumerable<T>. Асинхронные потоки основаны на запросах на получение. Они работают по принципу получения каждого следующего доступного элемента в ходе перебора. Шаблон наблюдателя, напротив, подразумевает отправку новых доступных элементов подписчикам, выполняющим роль наблюдателей. Пакет System.Linq.Async содержит метод расширения ToObservable, позволяющий преобразовать IAsyncEnumerable<T> в IObservable<T>.

Представим себе реализацию IObserver<SecretProperties>:

sealed file class SecretPropertyObserver : IObserver<SecretProperties>
{
    public void OnCompleted() =>
        Console.WriteLine("Done observing secrets");

    public void OnError(Exception error) =>
        Console.WriteLine($"Error observing secrets: {error}");

    public void OnNext(SecretProperties secret) =>
        Console.WriteLine($"Observable: {secret.Name}");
}

Метод расширения ToObservable можно использовать следующим образом:

IDisposable UseTheToObservableMethod()
{
    AsyncPageable<SecretProperties> allSecrets =
        client.GetPropertiesOfSecretsAsync();

    IObservable<SecretProperties> observable = allSecrets.ToObservable();

    return observable.Subscribe(
        new SecretPropertyObserver());
}

В приведенном выше коде C#:

  • Вызывается метод SecretClient.GetPropertiesOfSecretsAsync, и возвращается объект AsyncPageable<SecretProperties>.
  • Вызывается метод ToObservable() для экземпляра AsyncPageable<SecretProperties>, который возвращает IObservable<SecretProperties>.
  • Создается подписка на observable, в которую передается реализация наблюдателя, и подписка возвращается вызывающему объекту.
  • Подписка — IDisposable. После удаления подписка завершается.

Перебор страничных элементов

Pageable<T> является синхронной версией AsyncPageable<T>, которую можно использовать с обычным циклом foreach.

void IterateWithPageable()
{
    Pageable<SecretProperties> allSecrets = client.GetPropertiesOfSecrets();

    foreach (SecretProperties secret in allSecrets)
    {
        Console.WriteLine($"IterateWithPageable: {secret.Name}");
    }
}

Внимание

Несмотря на доступность этого синхронного API, для оптимальной работы используйте асинхронные альтернативы.

См. также