Batch multiple point operations together with the Azure Cosmos DB for NoSQL SDK
The TransactionalBatch and TransactionalBatchResponse classes together are the key to composing and decomposing operations into a single logical step. Using these classes, you can write your code to perform multiple operations and then determine if they were completed successfully server-side.
In this lab, you’ll use the SDK, to perform two dual-item operations where you attempt to create two items as a single logical unit.
Prepare your development environment
If you have not already cloned the lab code repository for DP-420 to the environment where you’re working on this lab, follow these steps to do so. Otherwise, open the previously cloned folder in Visual Studio Code.
-
Start Visual Studio Code.
📝 If you are not already familiar with the Visual Studio Code interface, review the Getting Started documentation
-
Open the command palette and run Git: Clone to clone the
https://github.com/microsoftlearning/dp-420-cosmos-db-dev
GitHub repository in a local folder of your choice.💡 You can use the CTRL+SHIFT+P keyboard shortcut to open the command palette.
-
Once the repository has been cloned, open the local folder you selected in Visual Studio Code.
Create an Azure Cosmos DB for NoSQL account and configure the SDK project
-
In a new web browser window or tab, navigate to the Azure portal (
portal.azure.com
). -
Sign into the portal using the Microsoft credentials associated with your subscription.
-
Select + Create a resource, search for Cosmos DB, and then create a new Azure Cosmos DB for NoSQL account resource with the following settings, leaving all remaining settings to their default values:
Setting Value Subscription Your existing Azure subscription Resource group Select an existing or create a new resource group Account Name Enter a globally unique name Location Choose any available region Capacity mode Provisioned throughput Apply Free Tier Discount Do Not Apply 📝 Your lab environments may have restrictions preventing you from creating a new resource group. If that is the case, use the existing pre-created resource group.
-
Wait for the deployment task to complete before continuing with this task.
-
Go to the newly created Azure Cosmos DB account resource and navigate to the Keys pane.
-
This pane contains the connection details and credentials necessary to connect to the account from the SDK. Specifically:
-
Notice the URI field. You will use this endpoint value later in this exercise.
-
Notice the PRIMARY KEY field. You will use this key value later in this exercise.
-
-
Return to Visual Studio Code.
-
In in the Explorer pane, browse to the 07-sdk-batch folder.
-
Open the script.cs code file within the 07-sdk-batch folder.
📝 The Microsoft.Azure.Cosmos library has already been pre-imported from NuGet.
-
Locate the string variable named endpoint. Set its value to the endpoint of the Azure Cosmos DB account you created earlier.
string endpoint = "<cosmos-endpoint>";
📝 For example, if your endpoint is: https://dp420.documents.azure.com:443/, then the C# statement would be: string endpoint = “https://dp420.documents.azure.com:443/”;.
-
Locate the string variable named key. Set its value to the key of the Azure Cosmos DB account you created earlier.
string key = "<cosmos-key>";
📝 For example, if your key is: fDR2ci9QgkdkvERTQ==, then the C# statement would be: string key = “fDR2ci9QgkdkvERTQ==”;.
-
Save the script.cs code file.
-
Open the context menu for the 07-sdk-batch folder and then select Open in Integrated Terminal to open a new terminal instance.
📝 This command will open the terminal with the starting directory already set to the 07-sdk-batch folder.
-
Add the Microsoft.Azure.Cosmos package from NuGet using the following command:
dotnet add package Microsoft.Azure.Cosmos --version 3.22.1
-
Build the project using the dotnet build command:
dotnet build
-
Close the integrated terminal.
Creating a transactional batch
First, let’s create a simple transactional batch that makes two fictional products. This batch will insert a worn saddle and a rusty handlebar into the container with the same “used accessories” category identifier. Both items have the same logical partition key, ensuring that we will have a successful batch operation.
-
Return to the editor tab for the script.cs code file.
-
Create a Product variable named saddle with a unique identifier of 0120, a name of Worn Saddle, and a category identifier of 9603ca6c-9e28-4a02-9194-51cdb7fea816:
Product saddle = new("0120", "Worn Saddle", "9603ca6c-9e28-4a02-9194-51cdb7fea816");
-
Create a Product variable named handlebar with a unique identifier of 012A, a name of Rusty Handlebar, and a category identifier of 9603ca6c-9e28-4a02-9194-51cdb7fea816:
Product handlebar = new("012A", "Rusty Handlebar", "9603ca6c-9e28-4a02-9194-51cdb7fea816");
-
Create a variable of type PartitionKey named partitionKey passing in 9603ca6c-9e28-4a02-9194-51cdb7fea816 as a constructor parameter:
PartitionKey partitionKey = new ("9603ca6c-9e28-4a02-9194-51cdb7fea816");
-
Invoke the CreateTransactionalBatch method of the container variable passing in the partitionkey variable as a method parameter and using the fluent syntax to invoke the CreateItem<> generic methods passing in the saddle and handlebar variables as items to create in individual operations and store the result in a variable named batch of type TransactionalBatch:
TransactionalBatch batch = container.CreateTransactionalBatch(partitionKey) .CreateItem<Product>(saddle) .CreateItem<Product>(handlebar);
-
Within a using statement, asynchronously invoke the ExecuteAsync method of the batch variable and store the result in a variable of type TransactionalBatchResponse named response:
using TransactionalBatchResponse response = await batch.ExecuteAsync();
-
Invoke the static Console.WriteLine method to output the value of the StatusCode property of the response variable:
Console.WriteLine($"Status:\t{response.StatusCode}");
-
Once you are done, your code file should now include:
using System; using Microsoft.Azure.Cosmos; string endpoint = "<cosmos-endpoint>"; string key = "<cosmos-key>"; CosmosClient client = new CosmosClient(endpoint, key); Database database = await client.CreateDatabaseIfNotExistsAsync("cosmicworks"); Container container = await database.CreateContainerIfNotExistsAsync("products", "/categoryId", 400); Product saddle = new("0120", "Worn Saddle", "9603ca6c-9e28-4a02-9194-51cdb7fea816"); Product handlebar = new("012A", "Rusty Handlebar", "9603ca6c-9e28-4a02-9194-51cdb7fea816"); PartitionKey partitionKey = new ("9603ca6c-9e28-4a02-9194-51cdb7fea816"); TransactionalBatch batch = container.CreateTransactionalBatch(partitionKey) .CreateItem<Product>(saddle) .CreateItem<Product>(handlebar); using TransactionalBatchResponse response = await batch.ExecuteAsync(); Console.WriteLine($"Status:\t{response.StatusCode}");
-
Save the script.cs code file.
-
In Visual Studio Code, open the context menu for the 07-sdk-batch folder and then select Open in Integrated Terminal to open a new terminal instance.
-
Build and run the project using the dotnet run command:
dotnet run
-
Observe the output from the terminal. The status code should be an OK.
-
Close the integrated terminal.
Creating an errant transactional batch
Now, let’s create a transactional batch that will error purposefully. This batch will attempt to insert two items that have different logical partition keys. We will create a flickering strobe light in the “used accessories” category and a new helmet in the “pristine accessories” category. By definition, this should be a bad request and return an error when performing this transaction.
-
Return to the editor tab for the script.cs code file.
-
Delete the following lines of code:
Product saddle = new("0120", "Worn Saddle", "9603ca6c-9e28-4a02-9194-51cdb7fea816"); Product handlebar = new("012A", "Rusty Handlebar", "9603ca6c-9e28-4a02-9194-51cdb7fea816"); PartitionKey partitionKey = new ("9603ca6c-9e28-4a02-9194-51cdb7fea816"); TransactionalBatch batch = container.CreateTransactionalBatch(partitionKey) .CreateItem<Product>(saddle) .CreateItem<Product>(handlebar); using TransactionalBatchResponse response = await batch.ExecuteAsync(); Console.WriteLine($"Status:\t{response.StatusCode}");
-
Create a Product variable named light with a unique identifier of 012B, a name of Flickering Strobe Light, and a category identifier of 9603ca6c-9e28-4a02-9194-51cdb7fea816:
Product light = new("012B", "Flickering Strobe Light", "9603ca6c-9e28-4a02-9194-51cdb7fea816");
-
Create a Product variable named helmet with a unique identifier of 012C, a name of New Helmet, and a category identifier of 0feee2e4-687a-4d69-b64e-be36afc33e74:
Product helmet = new("012C", "New Helmet", "0feee2e4-687a-4d69-b64e-be36afc33e74");
-
Create a variable of type PartitionKey named partitionKey passing in 9603ca6c-9e28-4a02-9194-51cdb7fea816 as a constructor parameter:
PartitionKey partitionKey = new ("9603ca6c-9e28-4a02-9194-51cdb7fea816");
-
Invoke the CreateTransactionalBatch method of the container variable passing in the partitionkey variable as a method parameter and using the fluent syntax to invoke the CreateItem<> generic methods passing in the light and helmet variables as items to create in individual operations and store the result in a variable named batch of type TransactionalBatch:
TransactionalBatch batch = container.CreateTransactionalBatch(partitionKey) .CreateItem<Product>(light) .CreateItem<Product>(helmet);
-
Within a using statement, asynchronously invoke the ExecuteAsync method of the batch variable and store the result in a variable of type TransactionalBatchResponse named response:
using TransactionalBatchResponse response = await batch.ExecuteAsync();
-
Invoke the static Console.WriteLine method to output the value of the StatusCode property of the response variable:
Console.WriteLine($"Status:\t{response.StatusCode}");
-
Once you are done, your code file should now include:
using System; using Microsoft.Azure.Cosmos; string endpoint = "<cosmos-endpoint>"; string key = "<cosmos-key>"; CosmosClient client = new CosmosClient(endpoint, key); Database database = await client.CreateDatabaseIfNotExistsAsync("cosmicworks"); Container container = await database.CreateContainerIfNotExistsAsync("products", "/categoryId", 400); Product light = new("012B", "Flickering Strobe Light", "9603ca6c-9e28-4a02-9194-51cdb7fea816"); Product helmet = new("012C", "New Helmet", "0feee2e4-687a-4d69-b64e-be36afc33e74"); PartitionKey partitionKey = new ("9603ca6c-9e28-4a02-9194-51cdb7fea816"); TransactionalBatch batch = container.CreateTransactionalBatch(partitionKey) .CreateItem<Product>(light) .CreateItem<Product>(helmet); using TransactionalBatchResponse response = await batch.ExecuteAsync(); Console.WriteLine($"Status:\t{response.StatusCode}");
-
Save the script.cs code file.
-
In Visual Studio Code, open the context menu for the 07-sdk-batch folder and then select Open in Integrated Terminal to open a new terminal instance.
-
Build and run the project using the dotnet run command:
dotnet run
-
Observe the output from the terminal. The status code should either be a Bad Request or Conflict. This occured because all items within the transaction did not share the same partition key value as the transactional batch.
-
Close the integrated terminal.
-
Close Visual Studio Code.