Infrastructure.Data.CosmosDb
10.0.0
Infrastructure.Data.CosmosDb 10.2.1
Additional Details Version 10.0.0 has a critical partition-key bug: Add, Update and
DeleteBy(entity) always use the document id as the partition key value.
Writes to a container whose partition key is not /id therefore fail with
HTTP 400 or run as silent cross-partition operations (data may land in the
wrong partition). Upgrade to 10.2.1 or later.
Note this is a breaking upgrade: the dynamic-id overloads GetByID(dynamic),
FindByID(dynamic) and DeleteBy(dynamic) now throw InvalidOperationException
when a partition key is configured — switch to the (id, partitionKeyValue)
overloads. See the CHANGELOG for the full migration.
See the version list below for details.
dotnet add package Infrastructure.Data.CosmosDb --version 10.0.0
NuGet\Install-Package Infrastructure.Data.CosmosDb -Version 10.0.0
<PackageReference Include="Infrastructure.Data.CosmosDb" Version="10.0.0" />
<PackageVersion Include="Infrastructure.Data.CosmosDb" Version="10.0.0" />
<PackageReference Include="Infrastructure.Data.CosmosDb" />
paket add Infrastructure.Data.CosmosDb --version 10.0.0
#r "nuget: Infrastructure.Data.CosmosDb, 10.0.0"
#:package Infrastructure.Data.CosmosDb@10.0.0
#addin nuget:?package=Infrastructure.Data.CosmosDb&version=10.0.0
#tool nuget:?package=Infrastructure.Data.CosmosDb&version=10.0.0
Infrastructure.Data.CosmosDb
A .NET 10.0 generic repository base class for Azure Cosmos DB, built on the Microsoft.Azure.Cosmos SDK v3.
Installation
dotnet add package Infrastructure.Data.CosmosDb
Configuration
Add the connection settings to your appsettings.json:
{
"CosmosDb": {
"Endpoint": "https://<your-account>.documents.azure.com:443/",
"Key": "<your-primary-key>",
"DatabaseId": "MyDatabase",
"CollectionId": "MyCollection",
"PartitionKey": "/id"
}
}
Register the settings in your application startup:
services.Configure<Settings>(configuration.GetSection("CosmosDb"));
Usage
1. Create your entity
The entity must expose a string property that maps to the Cosmos DB document id. The repository resolves it in this order:
- A property named
Id - A property named
id - Any property decorated with
[JsonPropertyName("id")]
The CosmosClient serializer must map the chosen property to the JSON field id. Using a camelCase naming policy covers options 1 and 2 automatically; [JsonPropertyName("id")] covers option 3 regardless of naming policy.
// Option 1 — property named Id (most common)
public class Order
{
public string Id { get; set; }
public string CustomerId { get; set; }
public decimal Total { get; set; }
}
// Option 2 — property named id
public class Order
{
public string id { get; set; }
}
// Option 3 — custom property name with JSON attribute
// With AddCosmosClient() (STJ):
public class Order
{
[JsonPropertyName("id")]
public string DocumentId { get; set; }
}
// With AddCosmosClientWithNewtonsoft():
public class Order
{
[Newtonsoft.Json.JsonProperty("id")]
public string DocumentId { get; set; }
}
2. Create your repository
Inherit from Repository<TEntity> and inject CosmosClient and IOptions<Settings>:
public class OrderRepository : Repository<Order>
{
public OrderRepository(CosmosClient client, IOptions<Settings> options)
: base(client, options) { }
}
To override the collection or partition key defined in configuration:
public class ProductRepository : Repository<Product>
{
public ProductRepository(CosmosClient client, IOptions<Settings> options)
: base(client, options, collectionId: "Products", partitionKey: "/category") { }
}
For containers without a partition key, pass an empty string:
public class LogRepository : Repository<LogEntry>
{
public LogRepository(CosmosClient client, IOptions<Settings> options)
: base(client, options, collectionId: "Logs", partitionKey: "") { }
}
3. Register and use
Register CosmosClient as a singleton using one of the provided extension methods:
// Program.cs
services.Configure<Settings>(configuration.GetSection("CosmosDb"));
// Option A — System.Text.Json with camelCase (recommended for new projects)
// Supports [JsonPropertyName] attributes
services.AddCosmosClient();
// Option B — Newtonsoft.Json with camelCase (for existing projects or preference)
// Supports [Newtonsoft.Json.JsonProperty] attributes
services.AddCosmosClientWithNewtonsoft();
// Both accept an optional delegate for additional configuration
services.AddCosmosClient(opt => opt.ConnectionMode = ConnectionMode.Gateway);
services.AddScoped<OrderRepository>();
// Usage
public class OrderService
{
private readonly OrderRepository _repository;
public OrderService(OrderRepository repository) => _repository = repository;
public Task<Order> GetOrder(string id) => _repository.GetByID(id);
public Task<IEnumerable<Order>> GetByCustomer(string customerId)
=> _repository.GetAll(o => o.CustomerId == customerId);
public async Task<string> CreateOrder(Order order)
=> (await _repository.Add(order))?.ToString();
public Task UpdateOrder(Order order) => _repository.Update(order, order.Id);
public Task DeleteOrder(string id) => _repository.DeleteBy(id);
}
Overriding behavior
All data access operations are delegated to protected virtual methods, making it easy to customize or test without a real Cosmos DB connection:
| Public method | Protected override |
|---|---|
GetByID |
ReadItemInternalAsync |
GetAll() |
QueryAllItemsInternalAsync |
GetAll(predicate) |
QueryItemsInternalAsync |
Add |
CreateItemInternalAsync |
Update |
ReplaceItemInternalAsync |
DeleteBy(id) |
DeleteItemInternalAsync |
public class CachedOrderRepository : Repository<Order>
{
private readonly IMemoryCache _cache;
public CachedOrderRepository(CosmosClient client, IOptions<Settings> options, IMemoryCache cache)
: base(client, options) => _cache = cache;
protected override async Task<Order> ReadItemInternalAsync(string id)
{
return await _cache.GetOrCreateAsync(id, _ => base.ReadItemInternalAsync(id));
}
}
Testing without Cosmos DB
Use the parameterless protected constructor and override the internal methods with an in-memory store:
class TestOrderRepository : Repository<Order>
{
private readonly Dictionary<string, Order> _store = new();
public TestOrderRepository() : base() { }
protected override Task<Order> ReadItemInternalAsync(string id)
{
_store.TryGetValue(id, out var item);
return Task.FromResult(item);
}
protected override Task<dynamic> CreateItemInternalAsync(Order item)
{
if (string.IsNullOrEmpty(item.Id)) item.Id = Guid.NewGuid().ToString();
_store[item.Id] = item;
return Task.FromResult<dynamic>(item.Id);
}
protected override Task DeleteItemInternalAsync(string id)
{
_store.Remove(id);
return Task.CompletedTask;
}
}
Running integration tests
Integration tests connect to a real Cosmos DB Emulator and are skipped automatically if it is not available — they never block the build.
Option 1 — Windows native emulator
winget install Microsoft.Azure.CosmosEmulator
# start via Start Menu or:
& "C:\Program Files\Azure Cosmos DB Emulator\CosmosDB.Emulator.exe"
Option 2 — Docker
docker compose up -d
Running the tests
# unit tests (always available, no emulator needed)
dotnet test Infrastructure.Data.CosmosDb.Tests
# integration tests (skipped if emulator is not running)
dotnet test Infrastructure.Data.CosmosDb.IntegrationTests
Notes
- The database and container are created automatically on first use if they do not exist.
- Default container throughput is 1000 RU/s. Override
CreateCollectionIfNotExistsAsyncto customize. Addgenerates a GUID id if the entity's id property is null or empty.- This implementation assumes the partition key value equals the document id. Override the protected methods if your partition strategy differs.
Addreturnsdynamic(interface contract) — call.ToString()to get the id as a string.- Entities must expose a string property that resolves to the Cosmos DB document
id— either namedId,id, or decorated with[JsonPropertyName("id")]. If none is found,AddandDeleteBy(entity)throwInvalidOperationException.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Infrastructure.Data.Abstractions (>= 8.0.0)
- Microsoft.Azure.Cosmos (>= 3.61.0)
- Microsoft.Extensions.Options (>= 10.0.8)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Migrated from deprecated DocumentDB SDK to Microsoft.Azure.Cosmos v3; upgraded to .NET 10.0; added CosmosClient injection support; added XML documentation; improved test infrastructure with overridable protected methods.