この記事では、Azure SDK for .NET 改ページ位置付け機能を使用して、大規模なデータ セットで効率的かつ生産的に動作する方法について説明します。 改ページは、大きなデータ セットをページに分割する処理であり、コンシューマーがより少量のデータを反復処理しやすくします。 C# 8 以降では、 非同期 (非同期) ストリームを使用して非同期的にストリームを作成および使用できます。 非同期ストリームは、 IAsyncEnumerable<T> インターフェイスに基づいています。 Azure SDK for .NET では、IAsyncEnumerable<T>
クラスを使用してAsyncPageable<T>
の実装を公開しています。
この記事のすべてのサンプルは、次の NuGet パッケージに依存しています。
- Azure.Security.KeyVault.Secrets
- Microsoft.Extensions.Azure
- Microsoft.Extensions.Hosting
- System.Linq.Async
Azure SDK for .NET パッケージの最新ディレクトリについては、 Azure SDK の最新リリースを参照してください。
ページング可能な戻り値の型
Azure SDK for .NET からインスタンス化されたクライアントは、次のページング可能な型を返すことができます。
タイプ | 説明 |
---|---|
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>
に変換します。 データが 1 つのページで返されない場合、このメソッドは複数のサービス呼び出しを行う可能性があります。
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 の代替手段を使用してください。
こちらも参照ください
.NET