Benday.CosmosDb
6.0.2-alpha
See the version list below for details.
dotnet add package Benday.CosmosDb --version 6.0.2-alpha
NuGet\Install-Package Benday.CosmosDb -Version 6.0.2-alpha
<PackageReference Include="Benday.CosmosDb" Version="6.0.2-alpha" />
<PackageVersion Include="Benday.CosmosDb" Version="6.0.2-alpha" />
<PackageReference Include="Benday.CosmosDb" />
paket add Benday.CosmosDb --version 6.0.2-alpha
#r "nuget: Benday.CosmosDb, 6.0.2-alpha"
#:package Benday.CosmosDb@6.0.2-alpha
#addin nuget:?package=Benday.CosmosDb&version=6.0.2-alpha&prerelease
#tool nuget:?package=Benday.CosmosDb&version=6.0.2-alpha&prerelease
Benday.CosmosDb
A collection of classes for implementing the domain model and repository patterns with Azure Cosmos DB. These classes are built using the Azure Cosmos DB libraries for .NET and aim to simplify using hierarchical partition keys and to make sure that query operations are created to use those partition keys correctly.
Written by Benjamin Day
Pluralsight Author | Microsoft MVP | Scrum.org Professional Scrum Trainer
https://www.benday.com
https://www.honestcheetah.com
info@benday.com
YouTube: https://www.youtube.com/@_benday
Key Features
- Interfaces and base classes for implementing the repository pattern with Cosmos DB
- Interfaces and base classes for implementing the domain model pattern with Cosmos DB
- Service layer abstractions (
ITenantItemService,IParentedItemService) - Support for parent-child entity relationships with
ParentedItemBaseandIParentedItem - Blob attachment support via
IBlobOwner-- connect Cosmos DB documents to files in Azure Blob Storage using Benday.BlobStorage - Help you to write LINQ queries against Cosmos DB without having to worry whether you're using the right partition keys
- Support for configuring repositories for use in ASP.NET Core projects
- Support for hierarchical partition keys
- Bulk operations with throttling and retry logic (
SaveAllAsync,DeleteAllByTenantIdAsync) - Paged query results with continuation tokens (
GetPagedAsync) - Shared interface contracts via
Benday.Common.Interfaces(IAsyncTenantRepository,IBlobOwner,IParentedItem) - Logging of query performance and request units
- Detect and warn when you have cross-partition queries
- Helper classes and methods for registering types and handling connection configuration
- Ultra-simple configuration for Azure Cosmos DB Linux emulator
- Optimistic concurrency control using ETags
Quick Start
For Azure Cosmos DB Emulator (Development)
{
"CosmosConfiguration": {
"UseEmulator": true
}
}
That's it! See EMULATOR-SETUP.md for complete emulator configuration guide.
For Production
Option 1: Using appsettings.json
{
"CosmosConfiguration": {
"DatabaseName": "ProductionDb",
"ContainerName": "ProductionContainer",
"CreateStructures": false,
"PartitionKey": "/tenantId,/entityType",
"HierarchicalPartitionKey": true,
"Endpoint": "https://your-cosmos.documents.azure.com:443/",
"UseDefaultAzureCredential": true
}
}
Option 2: Using CosmosConfigBuilder
var config = new CosmosConfigBuilder()
.WithEndpoint("https://your-cosmos.documents.azure.com:443/")
.UseDefaultAzureCredential()
.WithDatabase("ProductionDb")
.WithContainer("ProductionContainer")
.Build();
Quick Example
// Configure in Program.cs / Startup.cs
var cosmosConfig = builder.Configuration.GetCosmosConfig();
var cosmosBuilder = new CosmosRegistrationHelper(builder.Services, cosmosConfig);
// Register simple tenant item
cosmosBuilder.RegisterRepositoryAndService<Note>();
// Register parented item with parent-child relationships
cosmosBuilder.RegisterParentedRepositoryAndService<Comment>();
// Use in your services
public class CommentService
{
private readonly IParentedItemService<Comment> _commentService;
public CommentService(IParentedItemService<Comment> commentService)
{
_commentService = commentService;
}
public async Task<IEnumerable<Comment>> GetCommentsForNote(string tenantId, string noteId)
{
// Query comments by parent ID with entity type filtering
return await _commentService.GetAllByParentIdAsync(tenantId, noteId, "Note");
}
}
Custom Configuration Per Repository
You can register repositories with custom configuration values that override the defaults. This is useful when you need to store certain entity types in separate containers:
// Register LookupValue entities in a separate "LookupValues" container
cosmosBuilder.RegisterRepository<LookupValue, ILookupValueRepository, CosmosDbLookupValueRepository>(
containerName: "LookupValues",
withCreateStructures: true
);
Available configuration overrides:
connectionString- Use a different Cosmos DB endpointdatabaseName- Store in a different databasecontainerName- Store in a different containerpartitionKey- Use a different partition key pathuseHierarchicalPartitionKey- Override hierarchical partition key settinguseDefaultAzureCredential- Override authentication methodwithCreateStructures- Override auto-creation of database/container
Any parameter left as null will use the default value from the CosmosRegistrationHelper instance.
Blob Attachments with IBlobOwner and BlobBridge
Cosmos DB entities that inherit from TenantItemBase automatically implement IBlobOwner from Benday.Common.Interfaces. This means every entity can have file attachments stored in Azure Blob Storage via Benday.BlobStorage -- without adding any blob-specific code to the entity itself.
How It Works
TenantItemBase provides a default GetBlobPrefix() that organizes blobs by tenant and entity ID:
// TenantItemBase implements IBlobOwner with a default prefix
public abstract class TenantItemBase : CosmosIdentityBase, ITenantItem, IBlobOwner
{
public virtual string GetBlobPrefix()
{
return $"{TenantId}/{Id}/";
}
}
This means a Note entity with TenantId = "acme" and Id = "note-123" stores its blobs under the path acme/note-123/ in your blob container. You can override GetBlobPrefix() in your entity class for a different convention.
Setup
Add the Benday.BlobStorage and Benday.AzureStorage packages to your project:
dotnet add package Benday.AzureStorage --version 1.0.0-alpha
dotnet add package Benday.BlobStorage --version 1.0.0-alpha
Register Azure Storage services alongside your Cosmos DB services in Program.cs:
using Benday.CosmosDb.Utilities;
var cosmosConfig = builder.Configuration.GetCosmosConfig();
var cosmosBuilder = new CosmosRegistrationHelper(builder.Services, cosmosConfig);
// Register Cosmos DB repositories
cosmosBuilder.RegisterRepositoryAndService<Note>();
// Register Azure Storage services and a blob container
builder.Services.AddBendayAzureStorage(builder.Configuration);
builder.Services.AddBlobRepository("attachments");
Attaching Files to Cosmos DB Entities
Use BlobBridge<T> to manage file attachments for any entity that inherits from TenantItemBase:
using Benday.BlobStorage;
public class NoteAttachmentService
{
private readonly ITenantItemService<Note> _noteService;
private readonly BlobBridge<Note> _blobBridge;
public NoteAttachmentService(
ITenantItemService<Note> noteService,
IBlobRepository blobRepository)
{
_noteService = noteService;
_blobBridge = new BlobBridge<Note>(blobRepository);
}
// Upload a file attachment to a note
public async Task AttachFileAsync(string tenantId, string noteId, string filename, Stream content)
{
var note = await _noteService.GetByIdAsync(tenantId, noteId);
// Stores the blob at "{TenantId}/{Id}/{filename}"
// e.g., "acme/note-123/report.pdf"
await _blobBridge.AttachAsync(note, filename, content);
}
// List all attachments for a note
public async IAsyncEnumerable<string> ListAttachmentsAsync(Note note)
{
await foreach (var blob in _blobBridge.ListAttachmentsAsync(note))
{
yield return blob.Name;
}
}
// Download an attachment
public async Task<byte[]> DownloadAsync(Note note, string filename)
{
return await _blobBridge.DownloadAttachmentBytesAsync(note, filename);
}
// Get a time-limited SAS URL for downloading
public Uri GetDownloadUrl(Note note, string filename)
{
return _blobBridge.GetAttachmentSasUri(
note, filename,
expiry: TimeSpan.FromMinutes(15),
downloadFilename: filename);
}
// Delete all attachments when deleting a note
public async Task DeleteNoteWithAttachmentsAsync(string tenantId, string noteId)
{
var note = await _noteService.GetByIdAsync(tenantId, noteId);
// Delete blobs first, then the Cosmos DB document
await _blobBridge.DeleteAttachmentsAsync(note);
await _noteService.DeleteAsync(note);
}
}
Custom Blob Prefix
Override GetBlobPrefix() when you need a different blob path convention:
public class Invoice : TenantItemBase
{
public string InvoiceNumber { get; set; } = string.Empty;
protected override string GetEntityTypeName() => nameof(Invoice);
// Custom prefix: "acme/invoices/INV-2026-001/"
public override string GetBlobPrefix()
{
return $"{TenantId}/invoices/{InvoiceNumber}/";
}
}
Parent-Child Entities with Attachments
Parented entities also implement IBlobOwner since ParentedItemBase extends TenantItemBase. This lets you attach files to child entities with blob paths that naturally include the tenant context:
public class CommentAttachmentService
{
private readonly IParentedItemService<Comment> _commentService;
private readonly BlobBridge<Comment> _blobBridge;
public CommentAttachmentService(
IParentedItemService<Comment> commentService,
IBlobRepository blobRepository)
{
_commentService = commentService;
_blobBridge = new BlobBridge<Comment>(blobRepository);
}
public async Task AttachScreenshotAsync(Comment comment, Stream screenshot)
{
// Blob stored at "{TenantId}/{CommentId}/screenshot.png"
await _blobBridge.AttachAsync(comment, "screenshot.png", screenshot);
}
}
Bulk Operations
v6 adds throttled bulk operations with configurable concurrency and retry logic for handling Cosmos DB rate limiting (HTTP 429):
// Save many items with automatic throttling
await repository.SaveAllAsync(items);
// Customize concurrency and retries
await repository.SaveAllAsync(items, maxConcurrency: 10, maxRetries: 5);
// Delete all items for a tenant
await repository.DeleteAllByTenantIdAsync("acme");
Paged Queries
Retrieve large result sets efficiently with continuation tokens:
string? token = null;
do
{
var page = await repository.GetPagedAsync("acme", pageSize: 50, continuationToken: token);
foreach (var item in page.Items)
{
// Process each item
}
token = page.ContinuationToken;
} while (token != null);
Sample Application
A complete working sample application is included in this repository demonstrating all major features:
- Person Entity - Demonstrates
TenantItemBasewith custom repository and service layer implementations. Shows complex domain models with nested Address objects. - Note Entity - Simple
TenantItemBaseimplementation using default repository and service registrations. Serves as parent entity for Comments. - Comment Entity - Demonstrates
ParentedItemBasepattern showing parent-child relationships withParentIdandParentEntityTypefor type-safe queries. - LookupValue Entity - Demonstrates custom repository configuration to store entities in a separate Cosmos DB container using the
RegisterRepositoryoverload with configuration options.
To run the sample application:
- Start the Azure Cosmos DB Emulator (see EMULATOR-SETUP.md)
- Run
Benday.CosmosDb.SampleApp.WebUi - Explore the different entity patterns and their implementations
Upgrading from v5
See MIGRATION-v6.md for the complete migration guide. You can also use the migration guide as a prompt for AI coding agents (Claude Code, GitHub Copilot, etc.) to automate the code changes. Key changes:
OwnerIdrenamed toTenantId,DiscriminatorValuerenamed toEntityTypeIOwnedItem/OwnedItemBaserenamed toITenantItem/TenantItemBase- Default partition key path changed from
/pk,/discriminatorto/tenantId,/entityType - JSON serialization defaults to camelCase
- Use
cosmosmigrator migrateto migrate existing container data
Resources
Full Documentation
Sample Application - Working examples of all patterns
NuGet Package
Source Code
Repository API Documentation
Domain Model API Documentation
Feedback & Contributions
Got ideas for Cosmos DB functionality that you'd like to see? Found a bug? Let us know by submitting an issue. Want to contribute? Submit a pull request.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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. |
| .NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.1 is compatible. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- Azure.Identity (>= 1.20.0)
- Benday.Common (>= 9.12.0)
- Benday.Common.Interfaces (>= 1.0.1-alpha)
- Microsoft.Azure.Cosmos (>= 3.58.0)
- Microsoft.Extensions.Configuration (>= 10.0.5)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.5)
- Microsoft.Extensions.Options (>= 10.0.5)
- Newtonsoft.Json (>= 13.0.4)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Benday.CosmosDb:
| Package | Downloads |
|---|---|
|
Benday.Identity.CosmosDb
ASP.NET Core Identity implementation using Azure Cosmos DB as the backing store. Provides user store, role store, claims, lockout, two-factor authentication, and external login support. Built on top of the Benday.CosmosDb repository pattern library. |
|
|
Benday.Identity.CosmosDb.UI
ASP.NET Core Identity UI for Azure Cosmos DB. Provides pre-built Login/Logout/AccessDenied Razor Pages, a RedirectToLogin Blazor component, and AddCosmosIdentityWithUI() convenience method that combines core identity registration with cookie authentication. Built on top of Benday.Identity.CosmosDb. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 6.0.10 | 136 | 5/10/2026 |
| 6.0.9-alpha | 121 | 4/27/2026 |
| 6.0.8-alpha | 122 | 4/20/2026 |
| 6.0.7-alpha | 106 | 4/20/2026 |
| 6.0.3-alpha | 115 | 4/14/2026 |
| 6.0.2-alpha | 150 | 4/6/2026 |
| 6.0.0-alpha | 138 | 3/25/2026 |
| 5.3.0 | 166 | 3/12/2026 |
| 5.2.0 | 416 | 1/9/2026 |
| 5.1.0 | 319 | 12/16/2025 |
| 5.0.0 | 354 | 11/12/2025 |
| 4.10.0 | 252 | 11/6/2025 |
| 4.9.0 | 196 | 9/27/2025 |
| 4.8.0 | 390 | 8/8/2025 |
| 4.7.1 | 311 | 8/7/2025 |
| 4.6.1 | 268 | 6/14/2025 |
| 4.5.0 | 235 | 4/25/2025 |
| 4.4.0 | 231 | 3/22/2025 |
| 4.3.0 | 176 | 3/21/2025 |
| 4.2.2 | 184 | 3/21/2025 |
v6.0 - BREAKING: Renamed OwnerId to TenantId, DiscriminatorValue to EntityType, IOwnedItem to ITenantItem, OwnedItemBase to TenantItemBase, and all related types. Default partition key path changed from /pk,/discriminator to /tenantId,/entityType. JSON serialization defaults to camelCase. Added IBlobOwner support on TenantItemBase for blob attachments via Benday.BlobStorage. Added bulk SaveAllAsync and DeleteAllByTenantIdAsync with throttling and retry. Added paged queries with GetPagedAsync. Added shared interface contracts from Benday.Common.Interfaces. See MIGRATION-v6.md for upgrade guide;
v5.3 - Added some deprecation attributes to warn devs about some methods that ignore partition keys;
v5.2 - Added bulk delete support to repository;
v5.1 - Added per-repository custom container configuration support;
v5.0 - Adding IParentedItem interface and IParentedItemRepository interface plus CosmosDbParentedItemRepository to support items that have a parent-child relationship in CosmosDb;
v4.10 - Fixing bugs where etag and timestamp not getting updated on Save;
v4.9 - Added simplified config options for local emulator; Added configuration builder extension method to simplify configuration of cosmos db in web projects; Added better paging support to repository;
v4.8 - Refactored batch save to make the saving of each batch be a protected virtual method; Added batch size property;
v4.7 - Adding BeforeSaveBatch and AfterSaveBatch template methods;
v4.6 - Added configure option to use DefaultAzureCredential for authentication using managed service ids in azure; Fixed bug where cosmos client was improperly registered in DI when using managed ids;
v4.5 - Added option to configure 'allow bulk execution' for the CosmosClient; Default value for 'allow bulk execution' is now true;
v4.4 - Changed configuration reader to use default values rather than throwing exceptions when optional values are missing;
v4.3 - Fixed bug where UseHierarchicalPartitionKey was sometimes not being set;
v4.2.2 - Allowed cosmos db options UseHierarchicalPartitionKey to be set-able;
v4.2.1 - Minor bug fix release;
v4.2 - Fixed partition key bug on SaveAsync when in flat-partition key mode;
v4.1 - Added option to CosmosConfig.ConnectionString to enable GatewayMode via connection string;
v4.0 - Added support for non-hierarchical partition keys and made it the default; Added support for the beta version of the linux emulator container; Added configuration options for gateway mode and database throughput; Breaking change: renamed OwnedItemService interface and classes;
v3.0 - Changed framework target to be netstandard2.1; Added helper classes and methods to simplify type registrations;
v2.1 - Added optimistic concurrency checks using _etag to the repository SaveAsync method.
v2.0 - Changed implementation to use System.Text.Json for serialization instead of Newtonsoft.Json. Added configuration utilities to make it easier to configure CosmosDb in web projects. Added service layer base classes.
v1.0 - Adding initial version of the CosmosDb Domain Model & Repository utilities