Cosmos DB entity design with IContainerDocument, hierarchical partition keys, discriminator pattern, and ETag concurrency. Covers entity modeling for NoSQL documents. Trigger: cosmos entity, partition key, discriminator, IContainerDocument, NoSQL.
From dotnet-ai-kitnpx claudepluginhub faysilalshareef/dotnet-ai-kit --plugin dotnet-ai-kitThis skill uses the workspace's default tool permissions.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Delivers DB-free sandbox API regression tests for Next.js/Vitest to catch AI blind spots in self-reviewed code changes like API routes and backend logic.
IContainerDocument interfacePartitionKeyBuilderETag for optimistic concurrency controlid property is required by Cosmos DB (lowercase)namespace {Company}.{Domain}.Cosmos.Domain;
public interface IContainerDocument
{
string ContainerName { get; }
PartitionKey PartitionKeys { get; }
string Discriminator { get; }
string? ETag { get; set; }
}
namespace {Company}.{Domain}.Cosmos.Domain;
public sealed class SaleInvoice : IContainerDocument
{
public string id { get; set; } = string.Empty;
public string MerchantId { get; private set; } = string.Empty;
public string InvoiceNumber { get; private set; } = string.Empty;
public decimal TotalAmount { get; private set; }
public DateTime CreatedAt { get; private set; }
public List<SoldItem> Items { get; private set; } = [];
public int Sequence { get; private set; }
// IContainerDocument
public string ContainerName => "invoices";
public string Discriminator => nameof(SaleInvoice);
public string? ETag { get; set; }
public PartitionKey PartitionKeys => new PartitionKeyBuilder()
.Add(MerchantId)
.Add(CreatedAt.ToString("yyyy-MM"))
.Add(Discriminator)
.Build();
// Factory method from event
public static SaleInvoice FromInvoiceCreated(Event<InvoiceCreatedData> @event)
{
return new SaleInvoice
{
id = @event.AggregateId.ToString(),
MerchantId = @event.Data.MerchantId,
InvoiceNumber = @event.Data.InvoiceNumber,
TotalAmount = @event.Data.TotalAmount,
CreatedAt = @event.DateTime,
Items = @event.Data.Items.Select(i => new SoldItem
{
ProductId = i.ProductId,
ProductName = i.ProductName,
Quantity = i.Quantity,
UnitPrice = i.UnitPrice
}).ToList(),
Sequence = @event.Sequence
};
}
// Apply update event
public void Apply(Event<InvoiceUpdatedData> @event)
{
TotalAmount = @event.Data.TotalAmount;
Sequence = @event.Sequence;
}
}
namespace {Company}.{Domain}.Cosmos.Domain;
public sealed class SoldItem
{
public string ProductId { get; set; } = string.Empty;
public string ProductName { get; set; } = string.Empty;
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
public decimal LineTotal => Quantity * UnitPrice;
}
namespace {Company}.{Domain}.Cosmos.Domain;
public sealed class MerchantSalesReport : IContainerDocument
{
public string id { get; set; } = string.Empty;
public string MerchantId { get; private set; } = string.Empty;
public string ReportMonth { get; private set; } = string.Empty;
public decimal TotalSales { get; private set; }
public int InvoiceCount { get; private set; }
public bool IsReport => true;
public string ContainerName => "reports";
public string Discriminator => nameof(MerchantSalesReport);
public string? ETag { get; set; }
public PartitionKey PartitionKeys => new PartitionKeyBuilder()
.Add(MerchantId)
.Add(ReportMonth)
.Add(Discriminator)
.Build();
public void AddInvoice(decimal amount)
{
TotalSales += amount;
InvoiceCount++;
}
}
| Anti-Pattern | Correct Approach |
|---|---|
Missing id property (lowercase) | Cosmos requires lowercase id |
| Single partition key | Use hierarchical partition keys (up to 3 levels) |
| Missing Discriminator | Required for polymorphic container queries |
| Public setters everywhere | Private setters with factory methods and Apply |
| Large nested arrays (unbounded) | Keep nested arrays bounded; split if > 100 items |
# Find IContainerDocument implementations
grep -r "IContainerDocument" --include="*.cs" src/
# Find PartitionKeyBuilder usage
grep -r "PartitionKeyBuilder" --include="*.cs" src/
# Find Discriminator properties
grep -r "Discriminator =>" --include="*.cs" src/
# Find factory methods
grep -r "public static.*From" --include="*.cs" src/Cosmos/Domain/
IContainerDocument interface — match the contract exactlyid lowercase — Cosmos DB requirement