DEV Community

loading...
Cover image for Single Transaction for multiple DB Operations in Azure Cosmos DB

Single Transaction for multiple DB Operations in Azure Cosmos DB

rupeshtiwari profile image Rupesh Tiwari Originally published at rupeshtiwari.com on ・6 min read

Are you using repository pattern? And want to commit multiple repository operations as a single logical transaction in Azure Cosmos DB? Read this article to learn how can you combine all of your operations as single unit of work using TransactionalBatch.net class.

What is Transaction?

Transaction is the execution scope in the context of transaction processing. A transaction groups more than one database operations as a single commit. Therefore, you will notice single common outcome of your transaction. Either all operations passes or they all fail. They act as one thing. This is why atomicity is maintained.

Why to Combine operations as Single Transaction?

Imagine if you have repositories one for User and one for Address. And in a particular use case you want to update User Entity and Address Entity together. Suppose if Address update is failed you must fail User Entity update also. This is common use-case that every project needs that. In this article I will explain you how can you achieve this the bulk update in Cosmos Db.

Suppose you want to dynamically update some framework related database entity. Whenever some entity is updated. That time you can let repositories to just AddItem, DeleteItem or ReplaceItem or patchOperations. However, finally from the framework when you want to commit the transaction you have facility to add some other entities update and execute every thing together

Cosmos Db Bulk Operation Demo in C

In Cosmos Db Service, you can do bulk operations as a single transaction within a single container using partitionkey

TransactionalBatch Class in Cosmos DB

Using TransactionalBatch you can add multiple entity operations , batch them and perform operations across multiple entities (items) in the container. You must need a partition key to make it single transaction.

πŸ’‘ Note: In order to keep multiple operations transactional you must be within a single container and having one partition key.

Create Transactional Batch

You must first create a TransactionalBatch instance in a container by providing a partitionkey.

string businessType = "sales";

using (TransactionalBatchResponse batchResponse = await container.
CreateTransactionalBatch(
    new Cosmos.PartitionKey(businessType)
){}

Enter fullscreen mode Exit fullscreen mode

Defining Entities

public class User
{
    public string name { get; set; }
    public string id { get; set; }
    public number age { get; set; }
}


public class Address
{
    public string street { get; set; }
    public string id { get; set; }
    public string city { get; set; }
}

Enter fullscreen mode Exit fullscreen mode

Creating Multiple Entities as One Transaction in Cosmos DB

Let’s crate some entity that we want to save together using same partition key.

string businessType = "sales";

User harry = new User() {
  id = "user-1",
  name = "Harry",
  age = 23
};

Address singapore = new Address() {
  id = "address-1",
  city = "singapore city",
  street = "singapore road"
};

Enter fullscreen mode Exit fullscreen mode

Next we will create transaction batch and execute all operations asynchronously.

using (TransactionalBatchResponse batchResponse = await container.CreateTransactionalBatch(new Cosmos.PartitionKey(activityType))
    .CreateItem<User>(harry) 
     // πŸ‘† at this point user is not 
     // created in cosmos db. 
    .CreateItem<Address>(singapore)
    // πŸ‘† at this point address is not 
     // created in cosmos db. 
    .ExecuteAsync()) πŸ‘ˆ 
    /* once executeAsync is run all of 
      the operations are committed to 
      database as a one logical transation.
      Finally user and address is created.
    */
{
  // Read outcome values from **batchResponse**
}

Enter fullscreen mode Exit fullscreen mode

Reading Results of Batch Operation

Now you can read the outcome of bulk operation and retrieve each entity value from the batchResponse.

Reading User after creating in Cosmos DB

   // πŸ‘‡ Success Path
   // Look up harry via typed access on operation results
   TransactionalBatchOperationResult<User> harryResult = batchResponse.GetOperationResultAtIndex<User>(0);
   User harry = harryResult.Resource;

Enter fullscreen mode Exit fullscreen mode

Reading Address after creating in Cosmos DB

   // πŸ‘‡ Success Path
   // Look up address via typed access on operation results
   TransactionalBatchOperationResult<Address> addressResult = batchResponse.GetOperationResultAtIndex<Address>(0);
   Address harryAddress = addressResult.Resource;

Enter fullscreen mode Exit fullscreen mode

Handling Error after Batch Operation

In case of a failure , entire operations are cancelled and not committed. You will get the individual results with some status code from batchResponse.

// πŸ‘‡ Check Errors 
 if (!batchResponse.IsSuccessStatusCode)
   {
       // Handle and log exception
      TransactionalBatchOperationResult<User> harryResult = batchResponse.GetOperationResultAtIndex<User>(0);
      var statusCode = harryResult.StatusCode;
      // status code = 409 or 424 etc.
   }

Enter fullscreen mode Exit fullscreen mode

Final Source Code Creating Multiple Entities in Cosmos DB

string businessType = "sales";

User harry = new User() {
  id = "user-1",
  name = "Harry",
  age = 23
};

Address singapore = new Address() {
  id = "address-1",
  city = "singapore city",
  street = "singapore road"
};

using (TransactionalBatchResponse batchResponse = await container.CreateTransactionalBatch(new Cosmos.PartitionKey(activityType))
    .CreateItem<User>(harry)
    .CreateItem<Address>(singapore)
    .ExecuteAsync()) πŸ‘ˆ 
    /* once executeAsync is run all of 
      the operations are committed to 
      database as a one logical transation.
    */
{
  if (!batchResponse.IsSuccessStatusCode)
   {
       // Handle and log exception
      TransactionalBatchOperationResult<User> harryResult = batchResponse.GetOperationResultAtIndex<User>(0);
      var statusCode = harryResult.StatusCode;
      // status code = 409 or 424 etc.
      return;
   }

  TransactionalBatchOperationResult<Address> addressResult = batchResponse.GetOperationResultAtIndex<Address>(0);
  Address harryAddress = addressResult.Resource;

  TransactionalBatchOperationResult<Address> addressResult = batchResponse.GetOperationResultAtIndex<Address>(0);
  Address harryAddress = addressResult.Resource;
}

Enter fullscreen mode Exit fullscreen mode

Patch Operations in Cosmos DB with Bulk Action

You can also use PatchOperation class to update the property values of your entities as a bulk operation.

PatchOperation Class

PatchOperation class enables you to either replace a property or add dynamically new property in an entity.

Adding Dynamically new Property in Cosmos DB

I will demonstrate how you can crate zipCode property in the Address class after creating the Address entity in Cosmos Db. I will use PatchOperation.Add method.

List<PatchOperation> patchOperations = new List<PatchOperation>();
patchOperations.Add(PatchOperation.Add("/zipCode", 08452);

using (TransactionalBatchResponse batchResponse = await container.CreateTransactionalBatch(new Cosmos.PartitionKey(activityType))
    .CreateItem<User>(harry)
    .CreateItem<Address>(singapore)
    .PatchItem(singapore.id,patchOperations)πŸ‘ˆ 
     πŸ‘† 
    /* once executeAsync is run User and Address
    Will be created first then We will dynamically
    create zipCode property in the address object.
    */
    .ExecuteAsync())
{
}

Enter fullscreen mode Exit fullscreen mode

Replace existing Property of Item in Cosmos DB

Let’s say I want to change the age of Harry after creating in cosmos db. I will use PatchOperation.Replace method.

List<PatchOperation> patchOperations = new List<PatchOperation>();
patchOperations.Add(PatchOperation.Replace("/age", 28);

using (TransactionalBatchResponse batchResponse = await container.CreateTransactionalBatch(new Cosmos.PartitionKey(activityType))
    .CreateItem<User>(harry)
    .CreateItem<Address>(singapore)
    .PatchItem(harry.id,patchOperations)πŸ‘ˆ 
     πŸ‘† 
    /* once executeAsync is run User 
    and Address will be created
    first then replace the age to 28
    */
    .ExecuteAsync())
{
}

Enter fullscreen mode Exit fullscreen mode

Reading multiple Entities as Single Transaction

Sometime you wan to pull all entities from various items of cosmos database and show in a UI. Remember still you are within your partitionkey.

Fetching Multiple entities example

In this below example I am pulling all of the 4 users together.


string activityType = "customers";
using (TransactionalBatchResponse batchResponse = await container.CreateTransactionalBatch(new Cosmos.PartitionKey(User))
   .ReadItem("user-1")
   .ReadItem("user-2")
   .ReadItem("user-3")
   .ReadItem("user-4")
   .ExecuteAsync())
{
    // Look up interested results - eg. via direct access to operation result stream
    List<string> resultItems = new List<string>();
    foreach (TransactionalBatchOperationResult operationResult in batchResponse)
    {
        using (StreamReader streamReader = new StreamReader(operationResult.ResourceStream))
        {
            resultItems.Add(await streamReader.ReadToEndAsync());
        }
    }

    // **resultItems** will have all of the users 
}

Enter fullscreen mode Exit fullscreen mode

If you enjoyed this article then please share to your friends and if you have suggestions or thoughts to share with me then please write in the comment box.

Become full stack developer πŸ’»

I teach at Fullstack Master. If you want to become Software Developer and grow your carrier as new Software Engineer or Lead Developer/Architect. Consider subscribing to our full stack development training programs. You will learn Angular, RxJS, JavaScript, System Architecture and much more with lots of hands on coding. We have All-Access Monthly membership plans and you will get unlimited access to all of our video courses, slides , download source code & Monthly video calls.

  • Please subscribe to All-Access Membership PRO plan to access current and future angular, node.js and related courses.
  • Please subscribe to All-Access Membership ELITE plan to get everything from PRO plan. Additionally, you will get access to monthly live Q&A video call with Rupesh and you can ask doubts/questions and get more help, tips and tricks.

You bright future is waiting for you so visit today FullstackMaster and allow me to help you to board on your dream software company as a new Software Developer, Architect or Lead Engineer role.

πŸ’– Say πŸ‘‹ to me!

Rupesh Tiwari

Founder of Fullstack Master

Email: fullstackmaster1@gmail.com

Website: www.rupeshtiwari.com | www.fullstackmaster.net

Discussion (0)

pic
Editor guide