Excalibur.Data.CosmosDb
10.0.0-alpha.8
dotnet add package Excalibur.Data.CosmosDb --version 10.0.0-alpha.8
NuGet\Install-Package Excalibur.Data.CosmosDb -Version 10.0.0-alpha.8
<PackageReference Include="Excalibur.Data.CosmosDb" Version="10.0.0-alpha.8" />
<PackageVersion Include="Excalibur.Data.CosmosDb" Version="10.0.0-alpha.8" />
<PackageReference Include="Excalibur.Data.CosmosDb" />
paket add Excalibur.Data.CosmosDb --version 10.0.0-alpha.8
#r "nuget: Excalibur.Data.CosmosDb, 10.0.0-alpha.8"
#:package Excalibur.Data.CosmosDb@10.0.0-alpha.8
#addin nuget:?package=Excalibur.Data.CosmosDb&version=10.0.0-alpha.8&prerelease
#tool nuget:?package=Excalibur.Data.CosmosDb&version=10.0.0-alpha.8&prerelease
Excalibur.Data.CosmosDb
Azure Cosmos DB data provider implementation for Excalibur cloud-native data access.
Installation
dotnet add package Excalibur.Data.CosmosDb
Quick Start
Configuration
// In Program.cs or Startup.cs
services.AddCosmosDb(options =>
{
options.AccountEndpoint = "https://your-account.documents.azure.com:443/";
options.AccountKey = Environment.GetEnvironmentVariable("COSMOS_ACCOUNT_KEY");
options.DatabaseName = "your-database";
options.DefaultContainerName = "your-container";
});
// Or using configuration
services.AddCosmosDb(configuration.GetSection("CosmosDb"));
appsettings.json
{
"CosmosDb": {
"AccountEndpoint": "https://your-account.documents.azure.com:443/",
"AccountKey": "${COSMOS_ACCOUNT_KEY}",
"DatabaseName": "your-database",
"DefaultContainerName": "your-container",
"DefaultPartitionKeyPath": "/tenantId",
"UseDirectMode": true,
"MaxRetryAttempts": 9
}
}
Features
CRUD Operations
public class OrderService
{
private readonly CosmosDbPersistenceProvider _provider;
public OrderService(CosmosDbPersistenceProvider provider)
{
_provider = provider;
}
public async Task<Order?> GetOrderAsync(string orderId, string tenantId)
{
var partitionKey = new PartitionKey(tenantId, "/tenantId");
return await _provider.GetByIdAsync<Order>(orderId, partitionKey);
}
public async Task<CloudOperationResult<Order>> CreateOrderAsync(Order order)
{
var partitionKey = new PartitionKey(order.TenantId, "/tenantId");
return await _provider.CreateAsync(order, partitionKey);
}
public async Task<CloudOperationResult<Order>> UpdateOrderAsync(Order order, string etag)
{
var partitionKey = new PartitionKey(order.TenantId, "/tenantId");
return await _provider.UpdateAsync(order, partitionKey, etag);
}
}
Queries
public async Task<IReadOnlyList<Order>> GetOrdersByStatusAsync(
string tenantId,
string status)
{
var partitionKey = new PartitionKey(tenantId, "/tenantId");
var result = await _provider.QueryAsync<Order>(
"SELECT * FROM c WHERE c.status = @status",
partitionKey,
new Dictionary<string, object> { ["status"] = status });
return result.Documents;
}
Transactional Batches
public async Task<CloudBatchResult> ProcessOrderBatchAsync(
string tenantId,
Order order,
OrderLine[] lines)
{
var partitionKey = new PartitionKey(tenantId, "/tenantId");
var operations = new List<ICloudBatchOperation>
{
new CloudBatchCreateOperation(order.Id, order)
};
foreach (var line in lines)
{
operations.Add(new CloudBatchCreateOperation(line.Id, line));
}
return await _provider.ExecuteBatchAsync(partitionKey, operations);
}
Change Feed
public async Task SubscribeToChangesAsync(CancellationToken cancellationToken)
{
var subscription = await _provider.CreateChangeFeedSubscriptionAsync<Order>(
"orders",
new ChangeFeedOptions
{
StartPosition = ChangeFeedStartPosition.Now,
MaxBatchSize = 100
},
cancellationToken);
await foreach (var change in subscription.ReadChangesAsync(cancellationToken))
{
Console.WriteLine($"Change detected: {change.DocumentId}, Type: {change.EventType}");
// Process the change
}
}
Session Consistency
public async Task<Order?> ReadYourWriteAsync(Order order)
{
var partitionKey = new PartitionKey(order.TenantId, "/tenantId");
// Create returns session token
var createResult = await _provider.CreateAsync(order, partitionKey);
// Read with session consistency
var consistencyOptions = ConsistencyOptions.WithSession(createResult.SessionToken!);
return await _provider.GetByIdAsync<Order>(
order.Id,
partitionKey,
consistencyOptions);
}
Health Checks
services.AddHealthChecks()
.AddCosmosDb(
name: "cosmosdb",
tags: new[] { "database", "azure" });
Configuration Options
Connection Settings
| Option | Description | Default |
|---|---|---|
AccountEndpoint |
Cosmos DB account endpoint URI | Required* |
AccountKey |
Cosmos DB account key | Required* |
ConnectionString |
Alternative to endpoint + key | - |
DatabaseName |
Default database name | Required |
DefaultContainerName |
Default container name | - |
DefaultPartitionKeyPath |
Partition key path | /id |
Name |
Provider instance name for identification | CosmosDb |
ApplicationName |
Application name for diagnostics | - |
*Either ConnectionString OR both AccountEndpoint and AccountKey must be provided.
Performance Settings
| Option | Description | Default |
|---|---|---|
UseDirectMode |
Use direct connection mode (lower latency) | true |
MaxConnectionsPerEndpoint |
Maximum connections per endpoint | 50 |
RequestTimeoutInSeconds |
Request timeout | 60 |
IdleConnectionTimeoutInSeconds |
Idle connection timeout | 600 |
EnableTcpConnectionEndpointRediscovery |
Enable TCP connection reuse | true |
AllowBulkExecution |
Enable bulk operations | false |
BulkExecutionMaxDegreeOfParallelism |
Bulk operation parallelism | 25 |
Consistency and Reliability
| Option | Description | Default |
|---|---|---|
ConsistencyLevel |
Default consistency level | Account default |
MaxRetryAttempts |
Max retries on rate limiting (429) | 9 |
MaxRetryWaitTimeInSeconds |
Max wait between retries | 30 |
EnableContentResponseOnWrite |
Return document on write (set false to reduce RU) |
true |
PreferredRegions |
Preferred Azure regions for geo-redundancy | - |
Partition Key Strategies
Tenant-Based Partitioning
// Single value partition key
var pk = new PartitionKey("tenant-123", "/tenantId");
// Query within tenant
var orders = await _provider.QueryAsync<Order>(
"SELECT * FROM c WHERE c.status = 'pending'",
pk);
Composite Partition Keys
// For hierarchical partitioning
var pk = new CompositePartitionKey("/pk", "#", "TENANT", tenantId, "ORDER");
Local Development
For local development with Azure Cosmos DB Emulator:
# Install and start Cosmos DB Emulator
# Windows: Use the installer from Azure Portal
# Docker: name a specific emulator version — an unversioned tag can resolve to a different image later:
# docker run -p 8081:8081 -p 10251-10255:10251-10255 \
# mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-EN20260706
A client built from a container's connection string alone follows the account endpoint the emulator
advertises for itself, which is not the port the container was mapped to. Set LimitToEndpoint so the
client keeps using the endpoint it was given:
var options = new CosmosClientOptions
{
LimitToEndpoint = true,
ConnectionMode = ConnectionMode.Gateway,
SerializerOptions = new CosmosSerializationOptions
{
PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase,
},
};
using var client = new CosmosClient(connectionString, options);
SerializerOptions is not optional here. The Cosmos SDK's default serializer emits PascalCase property
names, so a client built without it writes Id where a later point-read looks for id, and the read
misses a document that is present. Every store in this package configures camelCase for that reason.
The container fixtures used by this project's own test suite live in its source tree rather than in a
published package. If you build from source, set EXCALIBUR_COSMOS_EMULATOR_IMAGE to run them against a different
emulator image — a newer release, or one built for an architecture the default does not serve — without
deriving a type of your own:
export EXCALIBUR_COSMOS_EMULATOR_IMAGE=mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-EN20260706
services.AddCosmosDb(options =>
{
options.AccountEndpoint = "https://localhost:8081/";
options.AccountKey = Environment.GetEnvironmentVariable("COSMOS_EMULATOR_KEY");
options.DatabaseName = "test-database";
});
Note: The emulator uses a well-known key for development. Never use this in production.
Error Handling
The provider returns CloudOperationResult<T> with status codes for handling errors:
var result = await _provider.UpdateAsync(order, partitionKey, etag);
switch (result.StatusCode)
{
case 200:
Console.WriteLine($"Success. RU charge: {result.RequestCharge}");
break;
case 404:
Console.WriteLine("Document not found");
break;
case 412:
Console.WriteLine("Precondition failed - ETag mismatch (concurrent modification)");
break;
case 429:
Console.WriteLine("Rate limited - retry with backoff");
break;
}
Common Status Codes
| Code | Description |
|---|---|
| 200 | Success |
| 201 | Created |
| 204 | No content (delete success) |
| 400 | Bad request (invalid query/document) |
| 404 | Document or container not found |
| 409 | Conflict (document already exists) |
| 412 | Precondition failed (ETag mismatch) |
| 429 | Rate limited (exceeded RU/s) |
| 503 | Service unavailable |
Best Practices
- Use Direct Mode for lowest latency in production
- Enable Session Consistency for read-your-writes scenarios
- Monitor Request Charges (RU/s) via
CloudOperationResult.RequestCharge - Use Transactional Batches for atomic operations within a partition
- Configure Preferred Regions for geo-redundant deployments
- Set
EnableContentResponseOnWrite = falseto reduce RU consumption when you don't need the response - Enable Bulk Execution for high-throughput batch operations
- Use connection pooling with appropriate
MaxConnectionsPerEndpointfor your workload
License
See LICENSE files in the repository root.
| 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
- Ben.Demystifier (>= 0.4.1)
- BenchmarkDotNet (>= 0.15.8)
- CloudNative.CloudEvents (>= 2.8.0)
- CloudNative.CloudEvents.SystemTextJson (>= 2.8.0)
- Cronos (>= 0.12.0)
- Dapper (>= 2.1.72)
- Excalibur.A3.Abstractions (>= 10.0.0-alpha.8)
- Excalibur.Cdc (>= 10.0.0-alpha.8)
- Excalibur.Data.Abstractions (>= 10.0.0-alpha.8)
- Excalibur.EventSourcing (>= 10.0.0-alpha.8)
- Excalibur.Saga (>= 10.0.0-alpha.8)
- FluentValidation (>= 12.1.1)
- FluentValidation.DependencyInjectionExtensions (>= 12.1.1)
- IdentityModel (>= 7.0.0)
- JsonNet.ContractResolvers (>= 2.0.0)
- Medo.Uuid7 (>= 3.2.0)
- MemoryPack (>= 1.21.4)
- Microsoft.ApplicationInsights (>= 3.1.0)
- Microsoft.AspNetCore.Authorization (>= 10.0.7)
- Microsoft.Azure.Cosmos (>= 3.58.0)
- Microsoft.CodeAnalysis.Analyzers (>= 5.3.0)
- Microsoft.CodeAnalysis.Common (>= 5.3.0)
- Microsoft.CodeAnalysis.CSharp (>= 5.3.0)
- Microsoft.Extensions.Caching.Abstractions (>= 10.0.10)
- Microsoft.Extensions.Caching.Memory (>= 10.0.10)
- Microsoft.Extensions.Configuration (>= 10.0.10)
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.10)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.10)
- Microsoft.Extensions.Configuration.CommandLine (>= 10.0.10)
- Microsoft.Extensions.Configuration.EnvironmentVariables (>= 10.0.10)
- Microsoft.Extensions.Configuration.Json (>= 10.0.10)
- Microsoft.Extensions.DependencyInjection (>= 10.0.10)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.10)
- Microsoft.Extensions.Diagnostics.HealthChecks (>= 10.0.10)
- Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions (>= 10.0.10)
- Microsoft.Extensions.Hosting (>= 10.0.10)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.10)
- Microsoft.Extensions.Http (>= 10.0.10)
- Microsoft.Extensions.Logging (>= 10.0.10)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.10)
- Microsoft.Extensions.Logging.Console (>= 10.0.10)
- Microsoft.Extensions.ObjectPool (>= 10.0.10)
- Microsoft.Extensions.Options (>= 10.0.10)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.10)
- Microsoft.Extensions.Options.DataAnnotations (>= 10.0.10)
- Microsoft.IdentityModel.Tokens (>= 8.17.0)
- MongoDB.Driver (>= 3.8.0)
- NCrontab (>= 3.4.0)
- Npgsql (>= 10.0.2)
- OpenTelemetry (>= 1.15.3)
- OpenTelemetry.Api (>= 1.15.3)
- OpenTelemetry.Extensions.Hosting (>= 1.15.3)
- Polly (>= 8.6.6)
- QuestPDF (>= 2026.2.4)
- SharpCompress (>= 0.48.0)
- Snappier (>= 1.3.1)
- System.IdentityModel.Tokens.Jwt (>= 8.17.0)
- System.IO.Hashing (>= 10.0.7)
- System.Threading.RateLimiting (>= 10.0.7)
NuGet packages (5)
Showing the top 5 NuGet packages that depend on Excalibur.Data.CosmosDb:
| Package | Downloads |
|---|---|
|
Excalibur.Outbox.CosmosDb
Azure Cosmos DB implementation of the cloud-native outbox pattern for Excalibur event sourcing. |
|
|
Excalibur.EventSourcing.CosmosDb
Azure Cosmos DB event store implementation for Excalibur event sourcing. |
|
|
Excalibur.Inbox.CosmosDb
Azure Cosmos DB implementation of the inbox pattern for Excalibur, providing idempotent message processing. |
|
|
Excalibur.Saga.CosmosDb
Azure Cosmos DB saga store implementation for Excalibur. |
|
|
Excalibur.Cdc.CosmosDb
Azure Cosmos DB Change Data Capture (CDC) implementation for Excalibur using change feed. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 10.0.0-alpha.8 | 0 | 8/14/2026 |
| 10.0.0-alpha.7 | 0 | 8/13/2026 |
| 10.0.0-alpha.6 | 57 | 8/11/2026 |
| 10.0.0-alpha.5 | 70 | 8/10/2026 |
| 10.0.0-alpha.4 | 64 | 8/10/2026 |
| 3.0.0-alpha.216 | 92 | 6/30/2026 |
| 3.0.0-alpha.215 | 93 | 6/23/2026 |
| 3.0.0-alpha.214 | 89 | 6/23/2026 |
| 3.0.0-alpha.208 | 85 | 6/11/2026 |
| 3.0.0-alpha.207 | 86 | 6/11/2026 |
| 3.0.0-alpha.205 | 93 | 6/10/2026 |
| 3.0.0-alpha.204 | 92 | 6/8/2026 |
| 3.0.0-alpha.203 | 90 | 6/8/2026 |
| 3.0.0-alpha.202 | 88 | 6/8/2026 |
| 3.0.0-alpha.201 | 91 | 6/8/2026 |
| 3.0.0-alpha.199 | 86 | 6/8/2026 |
| 3.0.0-alpha.198 | 84 | 5/28/2026 |
| 3.0.0-alpha.197 | 93 | 5/28/2026 |
| 3.0.0-alpha.194 | 88 | 5/20/2026 |
| 3.0.0-alpha.193 | 88 | 5/13/2026 |
See CHANGELOG.md for release notes