TypedItem is a .NET extension library for Azure Cosmos DB (SQL API) that adds typed item management, soft delete, and hierarchical type queries to any Cosmos DB container.
Each document stores a _type field computed from C# inheritance and [ItemType] attributes. This lets you safely store multiple item types in a single container and query them with full type safety.
dotnet add package TypedItemRequires Microsoft.Azure.Cosmos 3.x and .NET 10.
public class MyContainerItem : TypedItemBase
{
[JsonProperty("part")]
public string Part { get; set; }
public override PartitionKey GetPartitionKey()
=> CreatePartitionKey(Part);
}Annotate with [ItemType("name")]. Write operations require a sealed class.
[ItemType("person")]
public sealed class PersonItem : MyContainerItem
{
[JsonProperty("firstName")] public string FirstName { get; set; }
[JsonProperty("lastName")] public string LastName { get; set; }
}All methods extend Microsoft.Azure.Cosmos.Container and TransactionalBatch:
// Write
await container.CreateTypedItemAsync(person);
await container.UpsertTypedItemAsync(person);
await container.ReplaceTypedItemAsync(person, person.Id);
// Read — throws CosmosException (404) if soft-deleted or wrong type
var response = await container.ReadTypedItemAsync<PersonItem>(id, partitionKey);
// Soft-delete (sets _deleted = true via conditional PATCH)
await container.SoftDeleteTypedItemAsync(person);
// Query
var result = await container.QueryTypedItemAsync<PersonItem, PersonItem>(q => q);Use C# inheritance with multiple [ItemType] attributes to build dot-separated type hierarchies:
[ItemType("event")] // non-sealed = queryable parent
public class EventItem : MyContainerItem
{
[JsonProperty("date")] public DateTime Date { get; set; }
}
[ItemType("phonecall")] // _type stored as "event.phonecall"
public sealed class PhonecallItem : EventItem
{
[JsonProperty("duration")] public int Duration { get; set; }
}
[ItemType("meeting")] // _type stored as "event.meeting"
public sealed class MeetingItem : EventItem
{
[JsonProperty("attendees")] public string[] Attendees { get; set; }
}QueryTypedItemAsync automatically filters by type:
// Returns only phone calls
var calls = await container.QueryTypedItemAsync<PhonecallItem, PhonecallItem>(q => q);
// Returns ALL events (phone calls + meetings)
var events = await container.QueryTypedItemAsync<EventItem, EventItem>(q => q);| JSON field | Description |
|---|---|
_type |
Dot-separated type identifier (e.g., "event.phonecall") |
_deleted |
Soft-delete flag (true = logically deleted) |
id |
Document id (auto-generated if not set) |
- Getting Started — full usage guide with all options
- API Reference — all extension methods and types
- Running Tests — test infrastructure and known limitations