Разбиение на страницы с помощью Azure SDK для .NET
В этой статье вы узнаете, как использовать функции разбиения на страницы из пакета Azure SDK для .NET, чтобы удобно и эффективно работать с большими наборами данных. Разбиение на страницы — это разделение больших наборов данных по страницам, чтобы объекту-получателю было проще перебирать данные меньшего объема. Начиная с версии C# 8, вам доступно создание и использование асинхронных (async) потоков. Асинхронные потоки основаны на интерфейсе IAsyncEnumerable<T>. Пакет Azure SDK для .NET предоставляет реализацию IAsyncEnumerable<T>
со своим классом AsyncPageable<T>
.
Все примеры в этой статье используют следующие пакеты NuGet:
- Azure.Security.KeyVault.Secret
- Microsoft.Extensions.Azure
- Microsoft.Extensions.Hosting
- System.Linq.Async
Актуальный каталог пакетов 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#:
- Вызывается метод SecretClient.GetPropertiesOfSecretsAsync, и возвращается объект
AsyncPageable<SecretProperties>
. - Вызывается метод AsyncPageable<T>.GetAsyncEnumerator, который возвращает
IAsyncEnumerator<SecretProperties>
. - Многократно вызывается метод MoveNextAsync(), пока возвращаемые элементы не закончатся.
Перебор страниц 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, для оптимальной работы используйте асинхронные альтернативы.