Table of Contents
Azure Cosmos DB is a fully managed (PaaS) NoSQL which means it can handle unstructured data types. The Azure Cosmos DB is a relational database that is globally distributed and provides high availability and scalability, it provides you guaranteed speed and performance.
You can learn more about the repository pattern by first reading the Generic Repository Pattern C# article.
Creating an Azure Cosmos DB account
1. Type Cosmos DB into the Azure portal search bar and clicking
+ Create and choose Azure Cosmos DB for NoSQL as
shown in the following screenshot.

2. In the Create Azure Cosmos DB Account page, enter the basic settings for the new Azure Cosmos DB account as
shown in the following screenshot.

3. Select Review + create.
4. Navigate to Azure Cosmos DB Account and select the Keys menu. Copy
the URI and PRIMARY KEY values, which we need to connect
to the Cosmos DB account from our application.

Creating a class library project and adding Models
Our sample relational database for storing information about staff contain the following entities that will be stored in our azure cosmos db containers, as shown in the diagram below.

1. Open Visual Studio 2022 and click the Create a new project button.
2. Select the Class library project and click the Next button.
3. Enter CS.Staff.Models in the Project name textbox and Staff in the Solution name and click the Next button.
4. Select .NET 6.0 as the version of the Framework to use and click the Create button.
5. Right-click theCS.Staff.Models
project and add a new class file named Department
.
namespace CS.Staff.Models
{
public class Department
{
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
public string Name { get; set; }
public string Location { get; set; }
public List<Employee> Employees { get; set; }
[JsonProperty("_etag")]
public string Etag { get; set; }
}
}
6. Right-click theCS.Staff.Models
project and add a new class file named Employee
.
namespace CS.Staff.Models
{
public class Employee
{
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
public string FirstName { get;set; }
public string LastName { get; set; }
public string Job { get; set; }
public string Email { get; set; }
public DateTime HireDate { get; set; }
public decimal Salary { get; set; }
public List<Project> Projects { get; set; }
[JsonProperty("_etag")]
public string Etag { get; set; }
}
}
7. Right-click theCS.Staff.Models
project and add a new class file named Project
.
namespace CS.Staff.Models
{
public class Project
{
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
[JsonProperty("_etag")]
public string Etag { get; set; }
}
}
The id field is mandatory field for all entities.
Creating a Database and Containers
1. Navigate to the created Azure Cosmos DB account and Select Data Explorer and then select New Container.
2. Enter the settings for the Departments container as shown in the following screenshot.

We use /Name as the partition key in the Departments container.
3. Add the Employees container, as shown in the screenshot below.

We use /Email as the partition key in the Employees container.
4. Add the Projects container, as shown in the screenshot below.

We use /Id as the partition key in the Projects container.
Creating a class library and implementing the Repository pattern
The Repository pattern is a Domain-Driven Design pattern that provides an abstraction of data that separates the data layer from the rest of the application.
1. Right-click the solution and select the Add, New Project option from the menu.
2. Select the Class library project and click the Next button.
3. Enter CS.Staff.Repositories
in the Project name textbox and click the Next button.
4. Select .NET 6.0 as the version of the Framework to use and click the Create button.

5. Install the following NuGet package :
Microsoft.Azure.Cosmos
Also read https://dotnetcoder.com/creating-a-blazor-confirm-dialog-component/
Implementing the Repository pattern
1. Right click the CS.Staff.Repositories
project and add a new folder named Interfaces, and add an interface file named IBaseRepository<TItem>
namespace CS.Staff.Repositories.Interfaces
{
public interface IBaseRepository<TItem> where TItem : class
{
Task<IEnumerable<TItem>> GetItemsAsync(string filter);
Task<TItem> FindItemAsync(string id, string partionKey);
Task<TItem> AddItemAsync(TItem item, string partitionKey);
Task<TItem> UpdateItemAsync(TItem item, string id, string etag, string partitionKey);
Task<bool> RemoveItemAsync(string id, string partitionKey);
}
}
The generic IBaseRepository
is common interface for working with any of objects.
2. Add a new class file named BaseRepository<TItem>
to the project, that implements the IBaseRepository<TItem>
interface.
namespace CS.Staff.Repositories
{
public class BaseRepository<TItem> : IBaseRepository<TItem> where TItem : class
{
private readonly Container container;
public BaseRepository(CosmosClient cosmosClient, string databaseId, string containerId)
{
container = cosmosClient.GetContainer(databaseId, containerId);
}
public async Task<TItem> AddItemAsync(TItem item, string partitionKey)
{
return await container.CreateItemAsync<TItem>(item, new PartitionKey(partitionKey)).ConfigureAwait(false);
}
public async Task<TItem> FindItemAsync(string id, string partionKey)
{
try
{
ItemResponse<TItem> item = await container.ReadItemAsync<TItem>(id, new PartitionKey(partionKey)).ConfigureAwait(false);
return item;
} catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
return null;
}
}
public async Task<IEnumerable<TItem>> GetItemsAsync(string filter)
{
filter = string.IsNullOrEmpty(filter) ? "select * from item" : $"select * from item where {filter}";
var filteredFeed = container.GetItemQueryIterator<TItem>(new QueryDefinition(filter));
List<TItem> items = new();
while (filteredFeed.HasMoreResults)
{
var response = await filteredFeed.ReadNextAsync().ConfigureAwait(false);
items.AddRange(response.ToList());
}
return items;
}
public async Task<TItem> UpdateItemAsync(TItem item, string id, string etag, string partitionKey)
{
try
{
return await container.ReplaceItemAsync<TItem>(item, id, new PartitionKey(partitionKey), new ItemRequestOptions { IfMatchEtag = etag }).ConfigureAwait(false);
}
catch(CosmosException ex)
{
throw new InvalidOperationException(ex.Message);
}
}
public async Task<bool> RemoveItemAsync(string id, string partitionKey)
{
try
{
ItemResponse<TItem> item = await container.DeleteItemAsync<TItem>(id, new PartitionKey(partitionKey)).ConfigureAwait(false);
return true;
}
catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
return false;
}
}
}
}
Since we don’t need to access the original context, we use ConfigureAwait(false)
in all awaited methods.
ItemResponse<TItem> item = await container.ReadItemAsync<TItem>(id, new PartitionKey(partionKey)).ConfigureAwait(false);
We have declared a private variable of type Microsoft.Azure.Cosmos.Container
at the top that points to a corresponding container based on the containerId
parameter.
private readonly Container container;
The BaseRepository
constructor receives an instance of CosmosClient
using Dependency Injection, and then we initialize the private member container
withGetContainer(databaseId, containerId)
public BaseRepository(CosmosClient cosmosClient, string databaseId, string containerId)
{
container = cosmosClient.GetContainer(databaseId, containerId);
}
The other five methods are self-explanatory code that wraps around the Azure Cosmos container.
3. Right click the Interfaces folder and add three interface files named IDepartmentRepository
, IEmployeeRepository
and IProjectRepository
(see below).
namespace CS.Staff.Repositories.Interfaces
{
public interface IDepartmentRepository :IBaseRepository<Department>
{
}
}
namespace CS.Staff.Repositories.Interfaces
{
public interface IEmployeeRepository :IBaseRepository<Employee>
{
}
}
These interfaces do not add any functionality beyond what’s provided in the IBaseRepository
.
4. Right click the CS.Staff.Repositories
project and add a new class file named DatabaseSettings
as shown below.
namespace CS.Staff.Repositories
{
public class DatabaseSettings
{
public string DatabaseId { get; set; }
public string ContainerId { get; set; }
}
This class contains information related to the container and the database.
5. Right click the CS.Staff.Repositories
project and add a new class file named DepartmentRepository
as shown below.
namespace CS.Staff.Repositories
{
public class DepartmentRepository : BaseRepository<Department>, IDepartmentRepository
{
public DepartmentRepository(CosmosClient cosmosClient, DatabaseSettings settings)
: base(cosmosClient, settings.DatabaseId, settings.ContainerId)
{
}
}
}
The DepartmentRepository
implements the IBaseRepository
and the IDepartmentRepository
.
It receives CosmosClient
instance and DatabaseSettings
instance using Dependency Injection.
3. Similarly, dd two class files called EmployeeRepository
and ProjectRepository
(see below).
namespace CS.Staff.Repositories
{
public class EmployeeRepository :BaseRepository<Employee>, IEmployeeRepository
{
public EmployeeRepository(CosmosClient cosmosClient, DatabaseSettings settings)
: base(cosmosClient, settings.DatabaseId, settings.ContainerId)
{
}
}
}
namespace CS.Staff.Repositories
{
public class ProjectRepository : BaseRepository<Project>, IProjectRepository
{
public ProjectRepository(CosmosClient cosmosClient, DatabaseSettings settings)
: base(cosmosClient, settings.DatabaseId, settings.ContainerId)
{
}
}
}
Creating ASP.NET Core API
1. Create an ASP.NET Core Web API project using Visual Studio with the name CS.Staff.ApiApp

No we need to Add our azure cosmos db models and repositories packages to our Api App.
1. In Solution Explorer, right click the CS.Staff.ApiApp
project’s Dependencies node, and select Add Project Reference.
In the Reference Manager dialog, select the CS.Staff.Repositories project, and select OK.
2. Open the appsettings.Development.json file and add the information we need to connect
to the Cosmos DB account from our app URI , Primary Key , and container names .
{
"AccountEndpoint": "YOU ACCOUNT ENDPOINT",
"AuthKey": "YOUR PRIMARY KEY",
"DatabaseId": "cs-staff-database",
"DepartmentContainer": "Departments",
"EmployeeContainer": "Employees",
"ProjectContainer": "Projects"
}
3. Add a project folder named Extensions and add a new class file called RepositoryExtensions
namespace CS.Staff.ApiApp.Extensions
{
public static class RepositoryExtensions
{
public static IServiceCollection AddDepartmentRepository(this IServiceCollection services , Action<DatabaseSettings> configureSettings)
{
return services.AddScoped<IDepartmentRepository>(serviceProvider =>
{
var settings = new DatabaseSettings();
configureSettings(settings);
return ActivatorUtilities.CreateInstance<DepartmentRepository>(serviceProvider , settings);
});
}
public static IServiceCollection AddEmployeeRepository(this IServiceCollection services, Action<DatabaseSettings> configureSettings)
{
return services.AddScoped<IEmployeeRepository>(serviceProvider =>
{
var settings = new DatabaseSettings();
configureSettings(settings);
return ActivatorUtilities.CreateInstance<EmployeeRepository>(serviceProvider, settings);
});
}
public static IServiceCollection AddProjectRepository(this IServiceCollection services, Action<DatabaseSettings> configureSettings)
{
return services.AddScoped<IProjectRepository>(serviceProvider =>
{
var settings = new DatabaseSettings();
configureSettings(settings);
return ActivatorUtilities.CreateInstance<ProjectRepository>(serviceProvider, settings);
});
}
}
}
4. Add the repositories to the Dependency Injection Container with DatabaseSettings
configuration.
5. Add three controllers that will interact with azure cosmos db to the Controllers folder (see below).
DepartmentController
// Add services to the container.
builder.Services.AddControllers();
var configuration = builder.Configuration;
CosmosClientOptions cosmosClientOptions = new CosmosClientOptions()
{
SerializerOptions = new() { IgnoreNullValues = true },
};
builder.Services.AddSingleton(s => new CosmosClient(configuration.GetValue<string>("AccountEndpoint"), configuration.GetValue<string>("AuthKey"), cosmosClientOptions));
builder.Services.AddDepartmentRepository(settings =>
{
settings.DatabaseId = configuration.GetValue<string>("DatabaseId");
settings.ContainerId = configuration.GetValue<string>("DepartmentContainer"); ;
});
builder.Services.AddEmployeeRepository(settings =>
{
settings.DatabaseId = configuration.GetValue<string>("DatabaseId");
settings.ContainerId = configuration.GetValue<string>("EmployeeContainer"); ;
});
builder.Services.AddProjectRepository(settings =>
{
settings.DatabaseId = configuration.GetValue<string>("DatabaseId");
settings.ContainerId = configuration.GetValue<string>("ProjectContainer"); ;
});
namespace CS.Staff.ApiApp.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class DepartmentController : ControllerBase
{
private readonly IDepartmentRepository departmentRepository;
public DepartmentController(IDepartmentRepository departmentRepository)
{
this.departmentRepository = departmentRepository;
}
[HttpGet]
public async Task<IEnumerable<Department>> GetDepartmentsAsync(string filter)
{
return await departmentRepository.GetItemsAsync(filter).ConfigureAwait(false);
}
[HttpGet]
[Route("{id}")]
public async Task<Department> GetDepartmentById(string id, [FromQuery][Required] string partitionKey)
{
return await departmentRepository.FindItemAsync(id, partitionKey).ConfigureAwait(false);
}
[HttpPost]
public async Task<Department> CreateDepartmentAsync([FromBody] Department department)
{
if (department == null || department.Etag != null)
{
return null;
}
return await departmentRepository.AddItemAsync(department, department.Location).ConfigureAwait(false);
}
[HttpPut]
public async Task<Department> UpdateDepartmentAsync([FromBody] Department department)
{
return await departmentRepository.UpdateItemAsync(department, department.Id,department.Etag, department.Location ).ConfigureAwait(false);
}
[HttpDelete]
public async Task<bool> RemoveDepartmentAsync(string id, [FromQuery][Required] string partitionKey)
{
return await departmentRepository.RemoveItemAsync(id, partitionKey).ConfigureAwait(false);
}
}
}
EmployeeController
namespace CS.Staff.ApiApp.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class EmployeeController : ControllerBase
{
private readonly IEmployeeRepository employeeRepository;
public EmployeeController(IEmployeeRepository employeeRepository)
{
this.employeeRepository = employeeRepository;
}
[HttpGet]
public async Task<IEnumerable<Employee>> GetEmployeesAsync(string filter)
{
return await employeeRepository.GetItemsAsync(filter).ConfigureAwait(false);
}
[HttpGet]
[Route("{id}")]
public async Task<Employee> GetEmployeeById(string id, [FromQuery][Required] string partitionKey)
{
return await employeeRepository.FindItemAsync(id, partitionKey).ConfigureAwait(false);
}
[HttpPost]
public async Task<Employee> CreateEmployeeAsync([FromBody] Employee employee)
{
return await employeeRepository.AddItemAsync(employee, employee.Email).ConfigureAwait(false);
}
[HttpPut]
public async Task<Employee> UpdateEmployeeAsync([FromBody] Employee employee)
{
return await employeeRepository.UpdateItemAsync(employee, employee.Id, employee.Etag, employee.Email).ConfigureAwait(false);
}
[HttpDelete]
public async Task<bool> RemoveEmployeeAsync(string id, [FromQuery][Required] string partitionKey)
{
return await employeeRepository.RemoveItemAsync(id, partitionKey).ConfigureAwait(false);
}
}
}
ProjectController
namespace CS.Staff.ApiApp.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ProjectController : ControllerBase
{
private readonly IProjectRepository projectRepository;
public ProjectController(IProjectRepository projectRepository)
{
this.projectRepository = projectRepository;
}
[HttpGet]
public async Task<IEnumerable<Project>> GetProjectsAsync(string filter)
{
return await projectRepository.GetItemsAsync(filter).ConfigureAwait(false);
}
[HttpGet]
[Route("{id}")]
public async Task<Project> GetProjectById(string id, [FromQuery][Required] string partitionKey)
{
return await projectRepository.FindItemAsync(id, partitionKey).ConfigureAwait(false);
}
[HttpPost]
public async Task<Project> CreateProjectAsync([FromBody] Project project)
{
return await projectRepository.AddItemAsync(project, project.Id).ConfigureAwait(false);
}
[HttpPut]
public async Task<Project> UpdateProjectAsync([FromBody] Project project)
{
return await projectRepository.UpdateItemAsync(project, project.Id, project.Etag, project.Id).ConfigureAwait(false);
}
[HttpDelete]
public async Task<bool> RemoveProjectAsync(string id, [FromQuery][Required] string partitionKey)
{
return await projectRepository.RemoveItemAsync(id, partitionKey).ConfigureAwait(false);
}
}
}
6. Run the application and test it.

6. Create a Department with related Employees and retrieve it from the azure cosmos db database.


The code for the demo can be found Here
Also read https://dotnetcoder.com/creating-a-blazor-dropdown-list-component/
Enjoy This Blog?
Discover more from Dot Net Coder
Subscribe to get the latest posts sent to your email.