Infrastructure.Data.CosmosDb 10.0.0

Suggested Alternatives

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.

There is a newer version of this package available.
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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Infrastructure.Data.CosmosDb" Version="10.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Infrastructure.Data.CosmosDb" Version="10.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Infrastructure.Data.CosmosDb" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Infrastructure.Data.CosmosDb --version 10.0.0
                    
#r "nuget: Infrastructure.Data.CosmosDb, 10.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Infrastructure.Data.CosmosDb@10.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Infrastructure.Data.CosmosDb&version=10.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Infrastructure.Data.CosmosDb&version=10.0.0
                    
Install as a Cake Tool

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:

  1. A property named Id
  2. A property named id
  3. 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 CreateCollectionIfNotExistsAsync to customize.
  • Add generates 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.
  • Add returns dynamic (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 named Id, id, or decorated with [JsonPropertyName("id")]. If none is found, Add and DeleteBy(entity) throw InvalidOperationException.
Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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.