本文介绍如何使用 Azure SDK for .NET 分页功能高效高效地处理大型数据集。 分页是将大型数据集划分为页面的行为,使使用者更容易循环访问较小的数据量。 从 C# 8 开始,可以使用 异步(async)流异步创建和使用流。 异步流基于 IAsyncEnumerable<T> 接口。 用于 .NET 的 Azure SDK 公开其IAsyncEnumerable<T>
类的AsyncPageable<T>
实现。
本文中的所有示例都依赖于以下 NuGet 包:
- Azure.Security.KeyVault.Secrets
- Microsoft.Extensions.Azure
- Microsoft.Extensions.Hosting
- System.Linq.Async
有关适用于 .NET 包的 Azure SDK 的最新目录,请参阅 Azure SDK 最新版本。
可分页返回类型
从用于 .NET 的 Azure SDK 实例化的客户端可以返回以下可分页类型。
类型 | DESCRIPTION |
---|---|
Pageable<T> |
在页面中检索的值的集合 |
AsyncPageable<T> |
在页面中异步检索的值集合 |
本文中的大多数示例都是异步的,使用类型的变体 AsyncPageable<T>
。 对 I/O 绑定作使用异步编程是理想的。 一个完美的用例是使用 Azure SDK for .NET 中的异步 API,因为这些作表示 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
包提供一组对类型进行作的 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 可用,但请使用异步 API 替代项来获得更好的体验。