チュートリアル: Azure Cosmos DB for NoSQL で .NET コンソール アプリケーションを開発する
適用対象: NoSQL
Azure SDK for .NET を使用すると、非同期の個別操作またはトランザクション バッチを使用して、NoSQL コンテナーの API にデータを追加できます。 このチュートリアルでは、コンテナーに複数の項目を追加する新しい .NET コンソール アプリケーションを作成するプロセスについて説明します。
このチュートリアルでは、次の作業を行う方法について説明します。
- NoSQL 用 API を使用してデータベースを作成する
- .NET コンソール アプリケーションを作成し、Azure SDK for .NET を追加する
- NoSQL 用 API コンテナーに個々の項目を追加する
- NoSQL 用 API コンテナーから効率的に項目を取得する
- NoSQL 用 API コンテナー用にバッチ変更を含むトランザクションを作成する
前提条件
- 既存の Azure Cosmos DB for NoSQL アカウント。
- Azure サブスクリプションを既にお持ちの場合は、新しいアカウントを作成します。
- Azure サブスクリプションがない場合。 Azure Cosmos DB を無料で試すことができます。クレジット カードは必要ありません。
- Visual Studio Code
- .NET 8 以降
- C# アプリケーションの作成経験。
NoSQL 用 API リソースを作成する
まず、既存の NoSQL 用 API アカウントに空のデータベースを作成します。 後で Azure SDK for .NET を使用してコンテナーを作成します。
Azure portal で既存の NoSQL 用 API アカウントに移動します。
[リソース] メニューで [キー] を選択します。
[キー] ページで、[URI] フィールドと [PRIMARY KEY] フィールドの値を確認して記録します。 これらの値は、チュートリアル全体で使用されます。
リソース メニューで [Data Explorer] を選びます。
[データ エクスプローラー] ページのコマンド バーで [新しいデータベース] オプションを選択します。
[新しいデータベース] ダイアログで、次の設定を使用して新しいコンテナーを作成します。
値 データベース ID cosmicworks
データベースのスループットの種類 [手動] データベースのスループットの量 400
[OK] を選び、データベースを作成します。
.NET コンソール アプリケーションを作成する
次に、新しい .NET コンソール アプリケーションを作成し、NuGet の Microsoft.Azure.Cosmos
ライブラリを使用して Azure SDK for .NET をインポートします。
空のディレクトリでターミナルを開きます。
console
組み込みテンプレートを使用して新しいコンソール アプリケーションを作成しますdotnet new console --langVersion preview
NuGet から
Microsoft.Azure.Cosmos
パッケージの 3.31.1 プレビュー バージョンを追加します。dotnet add package Microsoft.Azure.Cosmos --version 3.31.1-preview
また、NuGet から
System.CommandLine
パッケージのプレリリース バージョンを追加します。dotnet add package System.CommandLine --prerelease
また、NuGet から
Humanizer
パッケージを追加します。dotnet add package Humanizer
コンソール アプリケーション プロジェクトをビルドします。
dotnet build
現在のプロジェクト フォルダーをワークスペースとして使用して Visual Studio Code を開きます。
ヒント
ターミナルで
code .
を実行して Visual Studio Code を開くと、現在のワークスペースとして作業ディレクトリを自動的に開くことができます。Program.cs ファイルに移動して開きます。 ファイル内の既存のコードをすべて削除します。
このコードをファイルに追加して、System.CommandLine ライブラリを使用して、
--first
および--last
のオプションを介して渡された 2 つの文字列のコマンド ラインを解析します。using System.CommandLine; var command = new RootCommand(); var nameOption = new Option<string>("--name") { IsRequired = true }; var emailOption = new Option<string>("--email"); var stateOption = new Option<string>("--state") { IsRequired = true }; var countryOption = new Option<string>("--country") { IsRequired = true }; command.AddOption(nameOption); command.AddOption(emailOption); command.AddOption(stateOption); command.AddOption(countryOption); command.SetHandler( handle: CosmosHandler.ManageCustomerAsync, nameOption, emailOption, stateOption, countryOption ); await command.InvokeAsync(args);
注意
このチュートリアルでは、コマンド ライン パーサーのしくみを理解することは必ずしも重要ではありません。 このパーサーには、アプリケーションの実行時に指定できる 4 つのオプションがあります。 これらのオプションのうち 3 つは、ID およびパーティション キー フィールドの構築に使用されるため、必須です。
この時点では、静的な
CosmosHandler.ManageCustomerAsync
メソッドをまだ定義していないため、プロジェクトはビルドされません。Program.cs ファイルを保存します。
SDK を使用してコンテナーに項目を追加する
次に、個々の操作を使用して、NoSQL 用 API コンテナーに項目を追加します。 このセクションでは、CosmosHandler.ManageCustomerAsync
メソッドを定義します。
新しい CosmosHandler.cs ファイルを作成します。
CosmosHandler.cs ファイル内に、
Humanizer
とMicrosoft.Azure.Cosmos
の名前空間の新しい using ディレクティブを追加します。using Humanizer; using Microsoft.Azure.Cosmos;
CosmosHandler
という名前の新しい静的クラスを作成します。public static class CosmosHandler { }
このアプリが機能することを確認するためだけに、静的な
ManageCustomerAsync
メソッドの短い実装を作成して、コマンド ライン入力を出力します。public static async Task ManageCustomerAsync(string name, string email, string state, string country) { await Console.Out.WriteLineAsync($"Hello {name} of {state}, {country}!"); }
CosmosHandler.cs ファイルを保存します。
ターミナルに戻り、アプリケーションを実行します。
dotnet run -- --name 'Mica Pereira' --state 'Washington' --country 'United States'
コマンドの出力は楽しいあいさつになります。
Hello Mica Pereira of Washington, United States!
CosmosHandler.cs ファイルに戻ります。
静的 CosmosHandler クラス内に、
_client
という名前のCosmosClient
型の新しいprivate static readonly
メンバーを追加します。private static readonly CosmosClient _client;
CosmosHandler
クラスの新しい静的コンストラクターを作成します。static CosmosHandler() { }
このコンストラクター内で、
CosmosClient
クラスの新しいインスタンスを作成し、ラボで前に記録した URI と PRIMARY KEY の値を含む 2 つの文字列パラメーターを渡します。 この新しいインスタンスを_client
メンバーに保存します。static CosmosHandler() { _client = new CosmosClient( accountEndpoint: "<uri>", authKeyOrResourceToken: "<primary-key>" ); }
静的 CosmosHandler クラス内に戻り、
Container
を返すGetContainerAsync
という名前の新しい非同期メソッドを作成します。private static async Task<Container> GetContainerAsync() { }
次の手順では、
GetContainerAsync
メソッド内にこのコードを追加します。cosmicworks
データベースを取得し、database
という名前の変数に保存します。Database database = _client.GetDatabase("cosmicworks");
階層パーティション キー パスのリスト内に
string
値の新しいジェネリックList<>
を作成し、それをkeyPaths
という名前の変数に保存します。List<string> keyPaths = new() { "/address/country", "/address/state" };
コンテナー (
customers
) の名前とパーティション キー パスのリストを使用して、新しいContainerProperties
変数を作成します。ContainerProperties properties = new( id: "customers", partitionKeyPaths: keyPaths );
CreateContainerIfNotExistsAsync
メソッドを使用して、コンテナー プロパティを指定し、コンテナーを取得します。 このメソッドは、その名前の通り、コンテナーがデータベース内にまだ存在しない場合に非同期で作成します。GetContainerAsync
メソッドの出力として結果を返します。return await database.CreateContainerIfNotExistsAsync( containerProperties: properties );
ManageCustomerAsync
メソッド内のすべてのコードを削除します。次の手順では、
ManageCustomerAsync
メソッド内にこのコードを追加します。GetContainerAsync
メソッドを非同期的に呼び出して、その結果をcontainer
という名前の変数に保存します。Container container = await GetContainerAsync();
id
という名前の新しい変数を作成します。これは Humanizer のKebaberize
メソッドを使用してname
メソッド パラメーターを変換します。string id = name.Kebaberize();
注意
Kebaberize
メソッドは、すべてのスペースをハイフンに置き換え、テキストを小文字に変換します。name
、state
、country
の各メソッド パラメーターとid
変数を使用して、新しい匿名型の項目を作成します。 項目をcustomer
という名前の変数として保存します。var customer = new { id = id, name = name, address = new { state = state, country = country } };
コンテナーの非同期
CreateItemAsync
メソッドを使用して、コンテナーに新しい項目を作成し、HTTP 応答メタデータをresponse
という名前の変数に割り当てます。var response = await container.CreateItemAsync(customer);
response
変数のStatusCode
およびRequestCharge
の各プロパティの値をコンソールに書き込みます。id
変数の値も書き込みます。Console.WriteLine($"[{response.StatusCode}]\t{id}\t{response.RequestCharge} RUs");
CosmosHandler.cs ファイルを保存します。
ターミナルに戻り、アプリケーションをもう一度実行します。
dotnet run -- --name 'Mica Pereira' --state 'Washington' --country 'United States'
コマンドの出力には、操作の状態と要求の料金が含まれます。
[Created] mica-pereira 7.05 RUs
注意
要求の料金は異なる場合があります。
アプリケーションをもう一度実行します。
dotnet run -- --name 'Mica Pereira' --state 'Washington' --country 'United States'
今回は、プログラムがクラッシュします。 エラー メッセージをスクロールすると、項目の一意識別子の競合が原因でクラッシュが発生したことがわかります。
Unhandled exception: Microsoft.Azure.Cosmos.CosmosException : Response status code does not indicate success: Conflict (409);Reason: ( Errors : [ "Resource with specified id or name already exists." ] );
SDK を使用して項目を取得する
コンテナーに最初の項目を作成したので、同じ SDK を使用してその項目を取得できます。 ここでは、項目に対してクエリとポイント読み取りを実行して、要求ユニット (RU) 消費量の違いを比較します。
CosmosHandler.cs ファイルに戻るか、開きます。
ManageCustomerAsync
メソッドから、最初の 2 行を除き、すべてのコード行を削除します。public static async Task ManageCustomerAsync(string name, string email, string state, string country) { Container container = await GetContainerAsync(); string id = name.Kebaberize(); }
次の手順では、
ManageCustomerAsync
メソッド内にこのコードを追加します。コンテナーの非同期
CreateItemAsync
メソッドを使用して、コンテナーに新しい項目を作成し、HTTP 応答メタデータをresponse
という名前の変数に割り当てます。var response = await container.CreateItemAsync(customer);
SQL クエリを使用して
sql
という名前の新しい文字列を作成し、フィルター (@id
) が一致する項目を取得します。string sql = @" SELECT * FROM customers c WHERE c.id = @id ";
query
という名前の新しいQueryDefinition
変数を作成し、唯一のクエリ パラメーターとしてsql
文字列を渡します。 また、WithParameter
fluid メソッドを使用して、変数id
の値を@id
パラメータに適用します。var query = new QueryDefinition( query: sql ) .WithParameter("@id", id);
GetItemQueryIterator<>
ジェネリック メソッドとquery
変数を使用して、Azure Cosmos DB からデータを取得する反復子を作成します。 この反復子をfeed
という名前の変数として保存します。 この式全体を using ステートメントでラップして、後で反復子を破棄します。using var feed = container.GetItemQueryIterator<dynamic>( queryDefinition: query );
feed
変数のReadNextAsync
メソッドを非同期的に呼び出し、その結果をresponse
という名前の変数に保存します。var response = await feed.ReadNextAsync();
response
変数のStatusCode
およびRequestCharge
の各プロパティの値をコンソールに書き込みます。id
変数の値も書き込みます。Console.WriteLine($"[{response.StatusCode}]\t{id}\t{response.RequestCharge} RUs");
CosmosHandler.cs ファイルを保存します。
ターミナルに戻り、アプリケーションを実行し、SQL クエリを使用して 1 つの項目を読み取ります。
dotnet run -- --name 'Mica Pereira' --state 'Washington' --country 'United States'
コマンドの出力には、クエリに複数の要求ユニット (RU) が必要であることが示されているはずです。
[OK] mica-pereira 2.82 RUs
CosmosHandler.cs ファイルに戻り、
ManageCustomerAsync
メソッドから最初の 2 行を除くすべてのコード行をもう一度削除します。public static async Task ManageCustomerAsync(string name, string email, string state, string country) { Container container = await GetContainerAsync(); string id = name.Kebaberize(); }
次の手順では、
ManageCustomerAsync
メソッド内にこのコードを追加します。state
とcountry
のパラメーターをマルチパート パーティション キーの値として追加して、PartitionKeyBuilder
の新しいインスタンスを作成します。var partitionKey = new PartitionKeyBuilder() .Add(country) .Add(state) .Build();
コンテナーの
ReadItemAsync<>
メソッドを使用して、id
とpartitionKey
の変数を使用してコンテナーから項目をポイント読み取りします。 結果をresponse
という名前の変数に保存します。var response = await container.ReadItemAsync<dynamic>( id: id, partitionKey: partitionKey );
response
変数のStatusCode
およびRequestCharge
の各プロパティの値をコンソールに書き込みます。id
変数の値も書き込みます。Console.WriteLine($"[{response.StatusCode}]\t{id}\t{response.RequestCharge} RU");
CosmosHandler.cs ファイルをもう一度保存します。
ターミナルに戻り、アプリケーションをもう 1 回実行して、1 つの項目をポイント読み取りします。
dotnet run -- --name 'Mica Pereira' --state 'Washington' --country 'United States'
コマンドの出力には、クエリに 1 つの RU が必要であることが示されているはずです。
[OK] mica-pereira 1 RUs
SDK を使用してトランザクションを作成する
最後に、作成した項目を取得し、その項目を読み取り、Azure SDK for .NET を使用して 1 つのトランザクションの一部として別の関連項目を作成します。
CosmosHandler.cs ファイルに戻るか、開きます。
ManageCustomerAsync
メソッドから次のコード行を削除します。var response = await container.ReadItemAsync<dynamic>( id: id, partitionKey: partitionKey ); Console.WriteLine($"[{response.StatusCode}]\t{id}\t{response.RequestCharge} RUs");
次の手順では、
ManageCustomerAsync
メソッド内にこのコードを追加します。name
、state
、country
の各メソッド パラメーターとid
変数を使用して、新しい匿名型の項目を作成します。 この項目をcustomerCart
という名前の変数として保存します。 この項目は、現在空の顧客のリアルタイムのショッピング カートを表します。var customerCart = new { id = $"{Guid.NewGuid()}", customerId = id, items = new string[] {}, address = new { state = state, country = country } };
name
、state
、country
のメソッド パラメーターとid
変数を使用して、別の新しい匿名型の項目を作成します。 この項目をcustomerCart
という名前の変数として保存します。 この項目は、顧客の発送先と連絡先情報を表します。var customerContactInfo = new { id = $"{id}-contact", customerId = id, email = email, location = $"{state}, {country}", address = new { state = state, country = country } };
コンテナーの
CreateTransactionalBatch
メソッドを使用して新しいバッチを作成し、partitionKey
変数を渡します。 このバッチをbatch
という名前の変数として保存します。 fluent メソッドを使用して、次のアクションを実行します。方法 パラメーター ReadItem
id
文字列変数CreateItem
customerCart
匿名型変数CreateItem
customerContactInfo
匿名型変数var batch = container.CreateTransactionalBatch(partitionKey) .ReadItem(id) .CreateItem(customerCart) .CreateItem(customerContactInfo);
バッチの
ExecuteAsync
メソッドを使用してトランザクションを開始します。 結果をresponse
という名前の変数に保存します。using var response = await batch.ExecuteAsync();
response
変数のStatusCode
およびRequestCharge
の各プロパティの値をコンソールに書き込みます。id
変数の値も書き込みます。Console.WriteLine($"[{response.StatusCode}]\t{response.RequestCharge} RUs");
CosmosHandler.cs ファイルをもう一度保存します。
ターミナルに戻り、アプリケーションをもう 1 回実行して、1 つの項目をポイント読み取りします。
dotnet run -- --name 'Mica Pereira' --state 'Washington' --country 'United States'
コマンドの出力には、トランザクション全体に使用される要求ユニットが表示されます。
[OK] 16.05 RUs
注意
要求の料金は異なる場合があります。
データ エクスプローラーの最終的なデータを検証する
最後に、Azure portal のデータ エクスプローラーを使用して、このチュートリアルで作成したデータとコンテナーを表示します。
Azure portal で既存の NoSQL 用 API アカウントに移動します。
リソース メニューで [Data Explorer] を選びます。
[データ エクスプローラー] ページで
cosmicworks
データベースを展開し、customers
コンテナーを選びます。コマンド バーで、[新しい SQL クエリ] を選びます。
クエリ エディターで、この SQL クエリ文字列を観察します。
SELECT * FROM c
[クエリの実行] を選択してクエリを実行し、結果を確認します。
結果には、このチュートリアルで作成された 3 つの項目を含む JSON 配列が含まれています。 すべての項目の階層パーティション キーの値は同じですが、ID フィールドは一意であることを確認してください。 含まれている出力例は、簡潔にするために省略されています。
[ { "id": "mica-pereira", "name": "Mica Pereira", "address": { "state": "Washington", "country": "United States" }, ... }, { "id": "33d03318-6302-4559-b5c0-f3cc643b2f38", "customerId": "mica-pereira", "items": [], "address": { "state": "Washington", "country": "United States" }, ... }, { "id": "mica-pereira-contact", "customerId": "mica-pereira", "email": null, "location": "Washington, United States", "address": { "state": "Washington", "country": "United States" }, ... } ]
リソースをクリーンアップする
このチュートリアルで使用したデータベースが不要になったら、削除してください。 これを行うには、アカウント ページに移動し、[データ エクスプローラー] を選択し、cosmicworks
データベースを選択して、[削除] を選択します。