events
Azure SDK for .NET での改ページ
この記事では、Azure SDK for .NET の改ページ機能を使用して、大規模なデータ セットを効率的かつ生産的に操作する方法について説明します。 改ページは、大きなデータ セットをページに分割して、コンシューマーがより少量のデータを反復処理しやすくする操作です。 C# 8 以降では、非同期ストリームを使用してストリームを非同期で作成および使用できます。 非同期ストリームは、IAsyncEnumerable<T> インターフェイスに基づいています。 Azure SDK for .NET により、その AsyncPageable<T>
クラスを使用して IAsyncEnumerable<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 からインスタンス化されたクライアントは、次のページング可能な型を返すことができます。
Type | 説明 |
---|---|
Pageable<T> |
ページで取得された値のコレクション |
AsyncPageable<T> |
ページで非同期で取得された値のコレクション |
この記事のほとんどのサンプルは、AsyncPageable<T>
型のバリエーションを使用した非同期のものです。 I/O バインド操作には非同期プログラミングを使用するのが理想的です。 特に最適な使用例は Azure SDK for .NET で非同期 API を使用する場合です。これらの操作は HTTP/S ネットワーク呼び出しを表すためです。
await foreach
構文を使用して AsyncPageable<T>
を反復処理するには、次の例を考えてみてください。
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
がコンソールに書き込まれます。
await foreach
構文が使用できない場合に AsyncPageable<T>
を反復処理するには、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<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 のセットがあり、これは同期
foreach
で反復処理されるIReadOnlyList<T>
を表します。 - 各ページには、次のページを要求するために使用できる Page<T>.ContinuationToken も含まれています。
System.Linq.Async
パッケージは、IAsyncEnumerable<T> 型で動作する LINQ メソッドのセットを提供します。 AsyncPageable<T>
では IAsyncEnumerable<T>
が実装されているため、System.Linq.Async
を使用してデータのクエリと変換を行うことができます。
AsyncPageable<T>
を List<T>
に変換するには、ToListAsync
を使用します。 このメソッドにより、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>
オブジェクトが返されます。 - 新しい
List<SecretProperties>
インスタンスを具体化するToListAsync
メソッドが待機されます。
Take
を使用して、AsyncPageable
の最初の N
要素のみを取得できます。 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
パッケージでは、IAsyncEnumerable<T>
を IObservable<T>
に変換できるようにする ToObservable
拡張メソッドが提供されています。
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>
は、通常の foreach
ループで使用できる AsyncPageable<T>
の同期バージョンです。
void IterateWithPageable()
{
Pageable<SecretProperties> allSecrets = client.GetPropertiesOfSecrets();
foreach (SecretProperties secret in allSecrets)
{
Console.WriteLine($"IterateWithPageable: {secret.Name}");
}
}
重要
この同期 API は使用可能ですが、エクスペリエンスを向上するために、非同期 API の代替手段を使用してください。
.NET に関するフィードバック
.NET はオープンソース プロジェクトです。 フィードバックを提供するにはリンクを選択します。
その他のリソース
トレーニング
モジュール
C# で配列と foreach ステートメントを使用して、データのシーケンスを格納し、反復処理する - Training
配列変数を作成し、配列の要素を反復処理する方法について説明します。