Exercise - Create new items

Completed

Recall that you can create items within your container using the Azure Cosmos DB SDK for .NET. For this project, the products container contains both the individual product items and special category items for each category. There's two cases you want to handle in this application:

  • If a category is empty, it's fine to just create that category's item individually. There are no related product items to create.
  • However, if a category includes related products, you want to create the category item and the related product items simultaneously.

Right now, you have two key requirements:

  1. Create an item individually as a single operation
  2. Use a transactional batch to create multiple related items

Illustration of icons indicating data being uploaded to the cloud.

After you complete this exercise, your project will have the logic to create items in your container either individually or as a batch.

Add an individual item to a container

In Azure Cosmos DB, you can create, replace, or upsert items to a container. Creating an item requires that the item has a unique identifier. Replacing an item requires that the item already exists. Upsert is the best of both worlds, where it checks the unique identifier, then replaces or creates the item. For this project, you want to be able to run the app multiple times without errors, making upsert a clear choice. For our first item, we create a category that doesn't have any associated products. Here, you implement a single upsert operation with a manually created category.

  1. Open the Program.cs file yet again.

  2. Create a new Category instance named goggles with the following values:

    Property Value
    id ef7fa0f1-0e9d-4435-aaaf-a778179a94ad
    categoryId gear-snow-goggles
    Category goggles = new(
        Id: "ef7fa0f1-0e9d-4435-aaaf-a778179a94ad",
        CategoryId: "gear-snow-goggles"
    );
    
  3. Create a new PartitionKey instance using the same value as the categoryId property for the Category instance you created earlier.

    PartitionKey gogglesKey = new("gear-snow-goggles");
    
  4. Use the UpsertItemAsync method to create or replace the item passing in an object for the item to create and a partition key value.

    Category result = await container.UpsertItemAsync(goggles, gogglesKey);
    
  5. Print various properties of result to the console including: The unique identifier of the item and the type of the item.

    Console.WriteLine($"[New item created]:\t{result.Id}\t(Type: {result.Type})");
    
  6. Create a new Category instance named helmets with the following values:

    Property Value
    id 91f79374-8611-4505-9c28-3bbbf1aa7df7
    categoryId gear-climb-helmets
    Category helmets = new(
        Id: "91f79374-8611-4505-9c28-3bbbf1aa7df7",
        CategoryId: "gear-climb-helmets"
    );
    
  7. Create a new PartitionKey instance using the same value as the categoryId property for the Category instance you created earlier.

    PartitionKey helmetsKey = new("gear-climb-helmets");
    
  8. Use the UpsertItemAsync method to create or replace the item. Pass in an object for the item to create and a partition key value. Return an object of type ItemResponse<T>.

    ItemResponse<Category> response = await container.UpsertItemAsync(helmets, helmetsKey);
    
  9. Print various properties of response to the console including: The unique identifier of the underlying item, the type of the underlying item, and the request charge in RUs.

    Console.WriteLine($"[New item created]:\t{response.Resource.Id}\t(Type: {response.Resource.Type})\t(RUs: {response.RequestCharge})");
    
  10. Save the Program.cs file.

Implement multiple operations as a transactional batch

Now, consider a scenario where you want to create multiple products along with a category. If the products are created, but the category doesn't exist, those products aren't nearly as useful. Creating multiple items is a situation where you can use a transaction to group multiple "point" operations together so they all succeed or fail as a single cohesive unit. Going back to our scenario, we need to create a category for outdoor tents with a few tent products. We already have a single category item without any product items. Here's what we should end up with:

Diagram of items in Azure Cosmos DB grouped by their partition key.

In this section, we create a transactional batch to create the tents category and related products together.

  1. In Program.cs, create a new Category instance named tents with the following values:

    Property Value
    id 5df21ec5-813c-423e-9ee9-1a2aaead0be4
    categoryId gear-camp-tents
    Category tents = new(
        Id: "5df21ec5-813c-423e-9ee9-1a2aaead0be4",
        CategoryId: "gear-camp-tents"
    );
    
  2. Create four instances of the Product type using these values.

    Property cirroa kuloar mammatin nimbolo
    Id e8dddee4-9f43-4d15-9b08-0d7f36adcac8 e6f87b8d-8cd7-4ade-a005-14d3e2fbd1aa f7653468-c4b8-47c9-97ff-451ee55f4fd5 6e3b7275-57d4-4418-914d-14d1baca0979
    CategoryId gear-camp-tents gear-camp-tents gear-camp-tents gear-camp-tents
    Name Cirroa Tent Kuloar Tent Mammatin Tent Nimbolo Tent
    Price 490.00 530.00 0.00 330.00
    Archived false false true false
    Quantity 15 8 0 35
    Product cirroa = new(
        Id: "e8dddee4-9f43-4d15-9b08-0d7f36adcac8",
        CategoryId: "gear-camp-tents"
    ){
        Name = "Cirroa Tent",
        Price = 490.00m,
        Archived = false,
        Quantity = 15
    };
    
    Product kuloar = new(
        Id: "e6f87b8d-8cd7-4ade-a005-14d3e2fbd1aa",
        CategoryId: "gear-camp-tents"
    ){
        Name = "Kuloar Tent",
        Price = 530.00m,
        Archived = false,
        Quantity = 8
    };
    
    Product mammatin = new(
        Id: "f7653468-c4b8-47c9-97ff-451ee55f4fd5",
        CategoryId: "gear-camp-tents"
    ){
        Name = "Mammatin Tent",
        Price = 0.00m,
        Archived = true,
        Quantity = 0
    };
    
    Product nimbolo = new(
        Id: "6e3b7275-57d4-4418-914d-14d1baca0979",
        CategoryId: "gear-camp-tents"
    ){
        Name = "Nimbolo Tent",
        Price = 330.00m,
        Archived = false,
        Quantity = 35
    };
    
  3. Now, create a new PartitionKey instance using the gear-camp-tents value.

    PartitionKey tentsKey = new("gear-camp-tents");
    
  4. Create a new transactional batch scoped to the gear-camp-tents partition key value using the CreateTransactionalBatch(PartitionKey) method. Using the fluent syntax, add five upsert operations to create the items we need in our container for the category and all of the related products.

    TransactionalBatch batch = container.CreateTransactionalBatch(tentsKey)
        .UpsertItem<Category>(tents)
        .UpsertItem<Product>(cirroa)
        .UpsertItem<Product>(kuloar)
        .UpsertItem<Product>(mammatin)
        .UpsertItem<Product>(nimbolo);
    
  5. Output a message to the console to indicating that we're starting a batch operation.

    Console.WriteLine("[Batch started]");
    
  6. Use the TransactionalBatch.ExecuteAsync method to execute the batch and return a special response type.

    using TransactionalBatchResponse batchResponse = await batch.ExecuteAsync();
    
  7. Using a for loop, iterate through all of the items in the response. First, convert each item to the type TransactionalBatchOperationResult using your Item base class as the generic. Then, print the unique identifier and type of the response object.

    for (int i = 0; i < batchResponse.Count; i++)
    {
        TransactionalBatchOperationResult<Item> batchResult = batchResponse.GetOperationResultAtIndex<Item>(i);
        Console.WriteLine($"[New item created]:\t{batchResult.Resource.Id}\t(Type: {batchResult.Resource.Type})");
    }
    
  8. Output another message to the console indicating that the batch is complete. Include the request charge for the entire batch in this message.

    Console.WriteLine($"[Batch completed]:\t(RUs: {batchResponse.RequestCharge})");
    
  9. Save the Program.cs file.

Check your work

Your app now creates multiple items and is designed to be resilient enough to be ran multiple times without causing an exception. Here, you run the application and check the output for the unique identifiers of each of the six newly created items.

  1. Run the .NET application in the terminal:

    dotnet run
    
  2. Observe the output of running the application. The output should match the example here:

    ...
    [New item created]:     ef7fa0f1-0e9d-4435-aaaf-a778179a94ad    (Type: Category)
    [New item created]:     91f79374-8611-4505-9c28-3bbbf1aa7df7    (Type: Category)        (RUs: 10.29)
    [Batch started]
    [New item created]:     5df21ec5-813c-423e-9ee9-1a2aaead0be4    (Type: Category)
    [New item created]:     e8dddee4-9f43-4d15-9b08-0d7f36adcac8    (Type: Product)
    [New item created]:     e6f87b8d-8cd7-4ade-a005-14d3e2fbd1aa    (Type: Product)
    [New item created]:     f7653468-c4b8-47c9-97ff-451ee55f4fd5    (Type: Product)
    [New item created]:     6e3b7275-57d4-4418-914d-14d1baca0979    (Type: Product)
    [Batch completed]:      (RUs: 36.76)
    

    Tip

    The RUs shown in this example output may vary from your output.