使用用于 .NET 的 Azure SDK 分页

本文介绍如何使用 Azure SDK for .NET 分页功能高效高效地处理大型数据集。 分页是将大型数据集划分为页面的行为,使使用者更容易循环访问较小的数据量。 从 C# 8 开始,可以使用 异步(async)流异步创建和使用流。 异步流基于 IAsyncEnumerable<T> 接口。 用于 .NET 的 Azure SDK 公开其IAsyncEnumerable<T>类的AsyncPageable<T>实现。

本文中的所有示例都依赖于以下 NuGet 包:

有关适用于 .NET 包的 Azure SDK 的最新目录,请参阅 Azure SDK 最新版本

可分页返回类型

从用于 .NET 的 Azure SDK 实例化的客户端可以返回以下可分页类型。

类型 DESCRIPTION
Pageable<T> 在页面中检索的值的集合
AsyncPageable<T> 在页面中异步检索的值集合

本文中的大多数示例都是异步的,使用类型的变体 AsyncPageable<T> 。 对 I/O 绑定作使用异步编程是理想的。 一个完美的用例是使用 Azure SDK for .NET 中的异步 API,因为这些作表示 HTTP/S 网络调用。

循环访问AsyncPageableawait 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 写入控制台。

循环访问AsyncPageablewhile

若要循环访问 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# 代码中:

System.Linq.AsyncAsyncPageable 配合使用

System.Linq.Async包提供一组对类型进行作的 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可用于仅获取第NAsyncPageable个元素。 使用 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 对应项等效的功能的其他方法。 此类方法的示例包括SelectWhereOrderByGroupBy

注意客户端评估

使用 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 可用,但请使用异步 API 替代项来获得更好的体验。

另请参阅