How to integrate Azure CosmosDB with .NET9

Mohamed Mohsen
6 min readMay 12, 2024

In continuation of the Azure integration series with .NET 9, you can explore all the previous articles on integrating Azure services with .NET 9 Every week, a new article is published. In this article, I’ll demonstrate how to integrate Azure Cosmos DB NoSQL with .NET 9.

Our Goal

We aim to establish a CosmosDB NoSQL database named store-db and set up a table within it named orders. Subsequently, we will utilize a .NET9 web API application to establish a connection with the database and execute all CRUD operations on the orders table.

Create CosmosDB NoSQL database

Let’s begin by opening the Azure Portal and searching for CosmosDB.

Azure portal

Create a new Azure Cosmos DB account.

Multiple options will appear. Select “Azure Cosmos DB for NoSQL.”

To learn more about the differences between each Cosmos DB account, you can read my Medium article “Azure CosmosDB: Types of Accounts Explained

Enter your desired name and select the capacity mode.

Azure Cosmos DB offers two capacity modes: provisioned throughput and serverless. While both modes allow you to execute the same database operations, they differ significantly in billing methods.

For a comprehensive overview of the differences between these modes, watch this video from the Microsoft Cosmos DB team.

I’ve chosen “Provisioned throughput” since it aligns with the free-tier subscription I’m currently using.

Keep the other defaults and proceed to create it.

After the database is successfully created, go to the created resource, then navigate to the “Data Explorer” and create a new container.

You can think of the container as the database table. Usually, when creating a new container, you need to select the database to which it belongs. Since this is the first time, you’ll find a section for creating a new database.

Let’s create an orders table and name our database store-db

Then, we need to set the most important thing, which is the “Partition key”. The partition key is used to automatically distribute data across partitions for scalability. Choose a property in your JSON document that has a wide range of values and evenly distributes request volume.

For small read-heavy or write-heavy workloads of any size, id is often a good choice.

Take your time to choose the right partition key for read-heavy or write-heavy workloads to avoid falling into the trap of hot partition issues.

A hot partition refers to a situation where one partition in a database receives significantly more traffic or requests than other partitions. This can cause performance bottlenecks, reduce system efficiency, and lead to delays in accessing data.

Next, set the unique ID, which is optional. Unique keys add a layer of data integrity to your database by ensuring the uniqueness of one or more values per partition key.

For demonstration purposes, I’m using the UserId as the partition key and OrderId as the unique ID. However, this setup may not be suitable for read-heavy or write-heavy applications.

Now our Azure CosmosDB NoSQL is configured and ready to be connected with the .NET application.

Before proceeding to connect to the .NET app, let’s prepare the CosmosDB database connection string, which will be needed for the integration. You can find it by navigating to “Keys” and copying the primary connection string.

Creating a .NET9 Web API and connecting it with CosmosDB

Let’s begin by initiating a new .NET9 Web API project. You can do this through Visual Studio or the CLI.

dotnet new webapi --framework net9.0 --output AzureCosmosDbNoSql

Next, add the necessary NuGet packages.

dotnet add package Microsoft.Azure.Cosmos
dotnet add package Microsoft.EntityFrameworkCore.Cosmos

Next, add the CosmosDB connection string in the appsettings.json or secrets.json. It’s recommended to store it in Azure Key Vault, which is considered the most secure way to manage secrets.

For more details on configuring and using Azure Key Vault, you can read my Medium article “Mastering Azure Key Vault Integration for .NET Web APIs”.

Then, inject the CosmosDB in the Program.cs

builder.Services.AddDbContext<AppDbContext>(options =>
{
options.UseCosmos(builder.Configuration["ConnectionStrings-CosmosDB"]!, "store-db");
});

Now, let’s establish the CRUD endpoints for our orders table.

Typically, nowadays CRUD operations are managed through a repository class. Here, I’ve set up a simple repository to mimic real-world scenarios.

public class OrdersRepository(AppDbContext context)
{
protected readonly AppDbContext _context = context;
protected readonly DbSet<Order> _dbset = context.Set<Order>();

public async Task<Order> GetAsync(Guid userId, Guid orderId, CancellationToken cancellationToken)
{
return await _dbset.FirstAsync(e => e.UserId == userId && e.OrderId == orderId, cancellationToken);
}

public async Task AddAsync(Order entity, CancellationToken cancellationToken) => await _dbset.AddAsync(entity, cancellationToken);
public void Update(Order entity) => _dbset.Update(entity);
public async Task DeleteAsync(Guid userId, Guid orderId, CancellationToken cancellationToken)
{
var entity = await GetAsync(userId, orderId, cancellationToken);
_dbset.Remove(entity);
}
public async Task SaveChangesAsync(CancellationToken cancellationToken) => await _context.SaveChangesAsync(cancellationToken);
}

Below is the OrdersController

[ApiController]
[Route("[controller]")]
public class OrdersController(OrdersRepository ordersRepository) : ControllerBase
{
private readonly OrdersRepository _ordersRepository = ordersRepository;

[HttpGet]
public async Task<ActionResult<Order>> Get(Guid userId, Guid orderId, CancellationToken cancellationToken)
{
return Ok(await _ordersRepository.GetAsync(userId, orderId, cancellationToken));
}

[HttpPost]
public async Task<ActionResult> Post([FromBody] Order order, CancellationToken cancellationToken)
{
await _ordersRepository.AddAsync(order, cancellationToken);
await _ordersRepository.SaveChangesAsync(cancellationToken);
return Ok();
}

[HttpPut]
public async Task<ActionResult> Put([FromBody] Order order, CancellationToken cancellationToken)
{
_ordersRepository.Update(order);
await _ordersRepository.SaveChangesAsync(cancellationToken);
return Ok();
}

[HttpDelete]
public async Task<ActionResult> Delete(Guid userId, Guid orderId, CancellationToken cancellationToken)
{
await _ordersRepository.DeleteAsync(userId, orderId, cancellationToken);
await _ordersRepository.SaveChangesAsync(cancellationToken);
return Ok();
}
}

Ensure to include the OrdersRepository in the dependency injection.

builder.Services.AddScoped<OrdersRepository>();

Your Program.cs should look something like this:

using AzureCosmosDbNoSql;
using AzureCosmosDbNoSql.Controllers;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<AppDbContext>(options =>
{
options.UseCosmos(builder.Configuration["ConnectionStrings-CosmosDB"]!, "store-db");
});

builder.Services.AddScoped<OrdersRepository>();

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

Now, let’s run the app and utilize Postman to test all the CRUD operations.

1- Add a new order

You can view the created order in Azure Cosmos DB by navigating to the “Data Explorer”, selecting your database, then choosing the table. Finally, click on “Items” to see all the added orders displayed.

2- Update the added order’s Total to be 700 instead of 500

3- Get an order with UserIdand OrderId

4- Delete an order with UserIdand OrderId

Now, you’ll notice that the orders table becomes empty after deleting the order from it.

If you enjoyed this article and are hungry for more .NET and Azure content, consider following me for regular updates and fresh insights into the world of .NET and Azure development.

+ Subscribe to get notified about new articles!

+ Follow me on Medium for more articles!

--

--

Mohamed Mohsen

Software Engineer @Microsoft | .NET | Azure Cloud Services | System Design | Architecture