Show / Hide Table of Contents

Class CosmosRepository<T>

Basic implementation of a Cosmos DB repository. Provides basic CRUD operations for a Cosmos DB entity, manages the container instance, and provides common functionality for custom queries as protected values and methods.

Inheritance
object
CosmosRepository<T>
CosmosTenantItemRepository<T>
Implements
IRepository<T>
Inherited Members
object.Equals(object)
object.Equals(object, object)
object.GetHashCode()
object.GetType()
object.MemberwiseClone()
object.ReferenceEquals(object, object)
object.ToString()
Namespace: Benday.CosmosDb.Repositories
Assembly: Benday.CosmosDb.dll
Syntax
public abstract class CosmosRepository<T> : IRepository<T> where T : class, ICosmosIdentity, new()
Type Parameters
Name Description
T

Domain model type managed by this repository

Remarks

The library supports two complementary ways to capture query diagnostics, and both fire for every event — using one does not disable the other:

  • Per-repository customization via overriding OnQueryDiagnostics(CosmosQueryDiagnostics). Use this when a specific repository needs non-default handling of its own events.
  • App-wide structured diagnostics via implementing and registering an ICosmosQueryLogSink in DI. Use this for cross-cutting behavior like structured log files, test captures, or production observability pipelines. A default NoOpCosmosQueryLogSink is registered automatically; consumers override it by calling CosmosRegistrationHelper.WithQueryLogSink or by registering their own implementation in the service collection before AddCosmosDb.

Diagnostic events are distinguished by EventKind — point operations, per-page feed responses, and query totals. Neither channel affects ILogger output, which fires unchanged regardless of how diagnostics are routed.

Constructors

| Edit this page View Source

CosmosRepository(IOptions<CosmosRepositoryOptions<T>>, CosmosClient, ILogger, ICosmosQueryLogSink?)

Constructor for the repository.

Declaration
public CosmosRepository(IOptions<CosmosRepositoryOptions<T>> options, CosmosClient client, ILogger logger, ICosmosQueryLogSink? sink = null)
Parameters
Type Name Description
IOptions<CosmosRepositoryOptions<T>> options

Configuration options

CosmosClient client

Cosmos Db client instance. NOTE: for performance reasons, this should probably be a singleton in the application.

ILogger logger

Logger instance.

ICosmosQueryLogSink sink

Optional app-wide diagnostics sink. When null (including when this constructor is called from a derived class that doesn't pass one), falls back to Instance. Register a real sink in DI via WithQueryLogSink<TSink>().

Exceptions
Type Condition
ArgumentException

Fields

| Edit this page View Source

_Options

Declaration
protected readonly CosmosRepositoryOptions<T> _Options
Field Value
Type Description
CosmosRepositoryOptions<T>
| Edit this page View Source

_sink

Sink that receives every CosmosQueryDiagnostics event the repository emits. Defaults to Instance when no sink is provided via constructor injection.

Declaration
protected readonly ICosmosQueryLogSink _sink
Field Value
Type Description
ICosmosQueryLogSink

Properties

| Edit this page View Source

BatchSize

Batch size for saving items to the Cosmos DB container. This is used to limit the number of items saved in a single batch. Default is 50 items per batch.

Declaration
protected virtual int BatchSize { get; set; }
Property Value
Type Description
int
| Edit this page View Source

EntityType

Get the entity type value for this repository. By default this is the class name for the domain model type managed by this repository.

Declaration
public virtual string EntityType { get; }
Property Value
Type Description
string
| Edit this page View Source

Logger

Declaration
protected ILogger Logger { get; }
Property Value
Type Description
ILogger

Methods

| Edit this page View Source

AfterSaveBatch(TransactionalBatchResponse, T[], int, int)

Declaration
protected virtual Task AfterSaveBatch(TransactionalBatchResponse response, T[] batch, int currentBatch, int batchCount)
Parameters
Type Name Description
TransactionalBatchResponse response
T[] batch
int currentBatch
int batchCount
Returns
Type Description
Task
| Edit this page View Source

BeforeSaveBatch(TransactionalBatch, T[], int, int)

Declaration
protected virtual Task BeforeSaveBatch(TransactionalBatch cosmosBatch, T[] batch, int currentBatch, int batchCount)
Parameters
Type Name Description
TransactionalBatch cosmosBatch
T[] batch
int currentBatch
int batchCount
Returns
Type Description
Task
| Edit this page View Source

DeleteAsync(string)

Delete an item from the Cosmos DB container.

Declaration
public Task DeleteAsync(string id)
Parameters
Type Name Description
string id

Id of the item

Returns
Type Description
Task
Exceptions
Type Condition
InvalidOperationException
| Edit this page View Source

ExecuteScalarAsync<TResult>(IQueryable<T>, Func<IQueryable<T>, Task<Response<TResult>>>, string, PartitionKey, Func<TResult, int>?)

Executes a scalar Cosmos SDK LINQ operation (such as CountAsync, FirstOrDefaultAsync, MaxAsync) through the library's diagnostics pipeline. The SDK's extension methods on IQueryable bypass this pipeline when called directly; this helper lets you call them while still capturing request charge, timing, query text, and cross-partition warnings in the same format as list queries.

Declaration
protected Task<TResult> ExecuteScalarAsync<TResult>(IQueryable<T> query, Func<IQueryable<T>, Task<Response<TResult>>> operation, string queryDescription, PartitionKey partitionKey, Func<TResult, int>? resultCountSelector = null)
Parameters
Type Name Description
IQueryable<T> query

The IQueryable<T> against which to execute the operation.

Func<IQueryable<T>, Task<Response<TResult>>> operation

A delegate that invokes the desired SDK extension method on the queryable and returns its Response<T>.

string queryDescription

Logging description for the query.

PartitionKey partitionKey

Partition key scope for the query.

Func<TResult, int> resultCountSelector

Optional: a delegate that maps the scalar result to an integer count for diagnostics purposes. Defaults to 1 if the result is non-null or a value type; 0 if the result is null. For most scalar operations the default is fine.

Returns
Type Description
Task<TResult>

The resource value from the SDK's Response<T>.

Type Parameters
Name Description
TResult

The return type of the SDK operation.

Remarks

The SQL text for diagnostics is extracted from the query via ToQueryDefinition<T>(IQueryable<T>). For some queryable shapes this extraction can fail; when it does, the query still executes normally and all other diagnostics fields (request charge, duration, result count, cross-partition) are populated. The QueryText field on the resulting event will be null in that case.

Examples
public async Task<int> GetCountAsync(string tenantId)
{
    var queryContext = await GetQueryContextAsync(tenantId);
    return await ExecuteScalarAsync(
        queryContext.Queryable,
        q => q.CountAsync(),
        GetQueryDescription(nameof(GetCountAsync)),
        queryContext.PartitionKey);
}
See Also
GetResultsAsync<TResult>(IQueryable<TResult>, string, PartitionKey)
| Edit this page View Source

GetAllAsync()

Get all items in the repository. NOTE: this almost certainly performs a cross-partition query and should be used with caution.

Declaration
public Task<IEnumerable<T>> GetAllAsync()
Returns
Type Description
Task<IEnumerable<T>>

The matching items

| Edit this page View Source

GetByIdAsync(string)

Get an item by its id. This method will return null if the item is not found. NOTE: this almost certainly performs a cross-partition query and should be used with caution because it does not use a partition key.

Declaration
public Task<T?> GetByIdAsync(string id)
Parameters
Type Name Description
string id
Returns
Type Description
Task<T>

The first matching entity

| Edit this page View Source

GetContainerAsync()

Get the container instance. This method will initialize the container if it is null.

Declaration
protected Task<Container> GetContainerAsync()
Returns
Type Description
Task<Container>

Reference to the container

Exceptions
Type Condition
InvalidOperationException
| Edit this page View Source

GetPagedAsync(int, string?)

Gets a page of results with continuation support for efficient large result set retrieval.

Declaration
[Obsolete("This overload performs a cross-partition query without a partition key. Use GetPagedAsync(string tenantId, ...) instead unless you explicitly need a cross-partition scan.")]
public virtual Task<PagedResults<T>> GetPagedAsync(int pageSize = 100, string? continuationToken = null)
Parameters
Type Name Description
int pageSize

Maximum number of items to return in this page

string continuationToken

Continuation token from previous query (null for first page)

Returns
Type Description
Task<PagedResults<T>>

A page of results with continuation information

| Edit this page View Source

GetPagedAsync(string, int, string?)

Gets a page of results for a specific partition with continuation support.

Declaration
protected virtual Task<PagedResults<T>> GetPagedAsync(string tenantId, int pageSize = 100, string? continuationToken = null)
Parameters
Type Name Description
string tenantId

Value to use for the first-level partition key (tenant id)

int pageSize

Maximum number of items to return in this page

string continuationToken

Continuation token from previous query (null for first page)

Returns
Type Description
Task<PagedResults<T>>

A page of results with continuation information

| Edit this page View Source

GetPartitionKey(string, string)

Get the partition key for an item.

Declaration
protected virtual PartitionKey GetPartitionKey(string tenantId, string entityType)
Parameters
Type Name Description
string tenantId

Top-level partition key value (tenant id)

string entityType

Second-level partition key value (entity type)

Returns
Type Description
PartitionKey
| Edit this page View Source

GetPartitionKey(T)

Get the partition key for an item.

Declaration
protected virtual PartitionKey GetPartitionKey(T item)
Parameters
Type Name Description
T item
Returns
Type Description
PartitionKey
| Edit this page View Source

GetQueryContextAsync()

Creates a query context for the repository WITHOUT a partition key, resulting in a cross-partition query. Prefer the overload that accepts a tenantId for partition-scoped queries.

Declaration
[Obsolete("This overload performs a cross-partition query without a partition key. Use GetQueryContextAsync(string tenantId) instead unless you explicitly need a cross-partition scan.")]
protected virtual Task<QueryContext<T>> GetQueryContextAsync()
Returns
Type Description
Task<QueryContext<T>>

A query context containing the LINQ queryable with an empty partition key.

Exceptions
Type Condition
InvalidOperationException

Thrown when the underlying LINQ queryable cannot be created.

| Edit this page View Source

GetQueryContextAsync(string)

Creates a query context for the repository with the specified partition key configuration. This is the starting point for all custom LINQ queries built off of this repository by child repository classes.

Declaration
protected virtual Task<QueryContext<T>> GetQueryContextAsync(string tenantId)
Parameters
Type Name Description
string tenantId

Value to use for the first-level partition key (tenant id).

Returns
Type Description
Task<QueryContext<T>>

A query context containing the LINQ queryable and its configured partition key.

| Edit this page View Source

GetQueryContextAsync(string, string)

Creates a query context for the repository with the specified partition key configuration. This is the starting point for all custom LINQ queries built off of this repository by child repository classes.

Declaration
protected virtual Task<QueryContext<T>> GetQueryContextAsync(string tenantId, string entityType)
Parameters
Type Name Description
string tenantId

Value to use for the first-level partition key (tenant id).

string entityType

Entity type value for the second-level partition key.

Returns
Type Description
Task<QueryContext<T>>

A query context containing the LINQ queryable and its configured partition key.

| Edit this page View Source

GetQueryDescription(string)

Gets a description for a query. By default, this will return the type name of the repository and the method name. By default, detect and use the method name of the caller.

Declaration
protected string GetQueryDescription(string methodName = "")
Parameters
Type Name Description
string methodName

Method that's calling the query

Returns
Type Description
string
| Edit this page View Source

GetQueryDescription(string, string)

Gets a description for a query. By default, this will return the type name of the repository and the method name as a formatted string.

Declaration
protected string GetQueryDescription(string typeName, string methodName)
Parameters
Type Name Description
string typeName

Name of the type

string methodName

Name of the method

Returns
Type Description
string

Formatted query description string

| Edit this page View Source

GetResultsAsync(QueryDefinition, string, PartitionKey)

Convenience overload of GetResultsAsync<TResult>(QueryDefinition, string, PartitionKey) for the common case where the result type is the repository's own entity type T.

Declaration
protected Task<List<T>> GetResultsAsync(QueryDefinition query, string queryDescription, PartitionKey partitionKey)
Parameters
Type Name Description
QueryDefinition query

The parameterized Cosmos SQL query to run.

string queryDescription

Logging description for the query.

PartitionKey partitionKey

Partition key scope for the query.

Returns
Type Description
Task<List<T>>

All matching items.

| Edit this page View Source

GetResultsAsync<TResult>(FeedIterator<TResult>, string, string?, IReadOnlyDictionary<string, object?>?, PartitionKey)

Reads all pages from a feed iterator, timing each page, accumulating totals, and emitting per-page and total diagnostics through OnQueryDiagnostics(CosmosQueryDiagnostics).

Declaration
protected Task<List<TResult>> GetResultsAsync<TResult>(FeedIterator<TResult> resultSetIterator, string queryDescription, string? queryText = null, IReadOnlyDictionary<string, object?>? parameters = null, PartitionKey partitionKey = default)
Parameters
Type Name Description
FeedIterator<TResult> resultSetIterator

Feed iterator to read the results from.

string queryDescription

Description of this query for logging.

string queryText

The generated SQL text (for diagnostics). Optional.

IReadOnlyDictionary<string, object> parameters

Query parameters (for diagnostics). Optional.

PartitionKey partitionKey

Partition key scope for the query (for diagnostics).

Returns
Type Description
Task<List<TResult>>

All items from the iterator.

Type Parameters
Name Description
TResult

Type of item produced by the feed iterator. Typically T, but may be a projected type when the underlying query selects into a different shape.

| Edit this page View Source

GetResultsAsync<TResult>(QueryDefinition, string, PartitionKey)

Executes a raw Cosmos SQL query and returns all results, logging the same diagnostics as the LINQ overload so raw-SQL queries and LINQ queries show up symmetrically in structured logs.

Declaration
protected Task<List<TResult>> GetResultsAsync<TResult>(QueryDefinition query, string queryDescription, PartitionKey partitionKey)
Parameters
Type Name Description
QueryDefinition query

The parameterized Cosmos SQL query to run.

string queryDescription

Logging description for the query.

PartitionKey partitionKey

Partition key scope for the query.

Returns
Type Description
Task<List<TResult>>

All matching items.

Type Parameters
Name Description
TResult

Type of item returned by the query. Typically T, but may be a projected type when the SQL query selects into a different shape.

Remarks

Use this when a query can't be expressed in LINQ — cross-apply joins over nested arrays, dynamic EXISTS clauses, VectorDistance, conditional aggregation, etc. The library's LINQ support handles the common case; this overload covers the cases it can't.

See Also
GetResultsAsync<TResult>(IQueryable<TResult>, string, PartitionKey)
| Edit this page View Source

GetResultsAsync<TResult>(IQueryable<TResult>, string, PartitionKey)

Executes a LINQ query and returns all results, logging per-page and total diagnostics through OnQueryDiagnostics(CosmosQueryDiagnostics).

Declaration
protected Task<List<TResult>> GetResultsAsync<TResult>(IQueryable<TResult> query, string queryDescription, PartitionKey partitionKey)
Parameters
Type Name Description
IQueryable<TResult> query

query to run

string queryDescription

logging description for the query

PartitionKey partitionKey

partition key that's configured for this query. NOTE: this is purely to logging purposes

Returns
Type Description
Task<List<TResult>>

All matching items.

Type Parameters
Name Description
TResult

Type of item returned by the query. Typically T, but may be a projected type when the LINQ query selects into a different shape.

See Also
GetResultsAsync<TResult>(QueryDefinition, string, PartitionKey)
ExecuteScalarAsync<TResult>(IQueryable<T>, Func<IQueryable<T>, Task<Response<TResult>>>, string, PartitionKey, Func<TResult, int>?)
| Edit this page View Source

Initialize()

Initializes the repository. This method will create the database and container if they don't already exist.

Declaration
protected Task Initialize()
Returns
Type Description
Task
| Edit this page View Source

IsCrossPartitionQuery(CosmosDiagnostics)

Attempt to determine if a query is a cross-partition query based on the diagnostics.

Declaration
protected virtual bool IsCrossPartitionQuery(CosmosDiagnostics diagnostics)
Parameters
Type Name Description
CosmosDiagnostics diagnostics

Diagnostics for a query response

Returns
Type Description
bool

True if it detects a cross-partition query.

| Edit this page View Source

LogFeedResponseDiagnostics(string, double, CosmosDiagnostics, TimeSpan, int, string?, IReadOnlyDictionary<string, object?>?, PartitionKey)

Logs diagnostics for a single page of feed iterator results (including cross-partition query detection) and fires OnQueryDiagnostics(CosmosQueryDiagnostics) with a FeedResponsePage event.

Declaration
protected bool LogFeedResponseDiagnostics(string queryDescription, double requestCharge, CosmosDiagnostics diagnostics, TimeSpan duration, int resultCount, string? queryText, IReadOnlyDictionary<string, object?>? parameters, PartitionKey partitionKey)
Parameters
Type Name Description
string queryDescription

Description of the query for log messages.

double requestCharge

RU charge for this page.

CosmosDiagnostics diagnostics

Cosmos diagnostics for this page.

TimeSpan duration

Wall-clock duration of this page's round trip.

int resultCount

Number of documents returned on this page.

string queryText

The generated SQL text.

IReadOnlyDictionary<string, object> parameters

Query parameters.

PartitionKey partitionKey

Partition key scope for this query.

Returns
Type Description
bool

Whether the page was detected as a cross-partition execution.

| Edit this page View Source

LogPointOperationDiagnostics(string, double, CosmosDiagnostics, TimeSpan)

Logs diagnostics for a point operation (save, delete, point-read) and fires OnQueryDiagnostics(CosmosQueryDiagnostics) with a PointOperation event.

Declaration
protected void LogPointOperationDiagnostics(string operationName, double requestCharge, CosmosDiagnostics diagnostics, TimeSpan duration)
Parameters
Type Name Description
string operationName

Name of the operation for log messages.

double requestCharge

RU charge from the response.

CosmosDiagnostics diagnostics

Cosmos diagnostics from the response.

TimeSpan duration

Wall-clock duration of the SDK round trip.

| Edit this page View Source

LogQueryTotalDiagnostics(string, double, TimeSpan, int, string?, IReadOnlyDictionary<string, object?>?, PartitionKey, bool)

Logs the total RU charge for a completed query and fires OnQueryDiagnostics(CosmosQueryDiagnostics) with a QueryTotal event.

Declaration
protected void LogQueryTotalDiagnostics(string queryDescription, double totalRequestCharge, TimeSpan totalDuration, int totalResultCount, string? queryText, IReadOnlyDictionary<string, object?>? parameters, PartitionKey partitionKey, bool isCrossPartition)
Parameters
Type Name Description
string queryDescription

Description of the query for log messages.

double totalRequestCharge

Accumulated RU charge across all pages.

TimeSpan totalDuration

Accumulated round-trip duration across all pages.

int totalResultCount

Total document count across all pages.

string queryText

The generated SQL text.

IReadOnlyDictionary<string, object> parameters

Query parameters.

PartitionKey partitionKey

Partition key scope for this query.

bool isCrossPartition

OR across all pages of the cross-partition flag.

| Edit this page View Source

OnQueryDiagnostics(CosmosQueryDiagnostics)

Called for every query execution event: point operations, feed response pages, and query totals. Override in derived classes to route diagnostics to additional sinks for THIS repository only. For app-wide routing use ICosmosQueryLogSink instead. The base ILogger output is not affected by overriding this.

Declaration
protected virtual void OnQueryDiagnostics(CosmosQueryDiagnostics diagnostics)
Parameters
Type Name Description
CosmosQueryDiagnostics diagnostics

Structured payload describing the event.

See Also
ICosmosQueryLogSink
| Edit this page View Source

SaveAsync(IList<T>)

Save a list of items to the Cosmos DB container. This method will perform an insert if the item does not exist, otherwise it will perform an update. Items are saved in batches of 50 by default.

Declaration
public virtual Task SaveAsync(IList<T> items)
Parameters
Type Name Description
IList<T> items

Items to save

Returns
Type Description
Task
Exceptions
Type Condition
Exception
| Edit this page View Source

SaveAsync(T)

Save an item to the Cosmos DB container. This method will perform an insert if the item does not exist, otherwise it will perform an update.

Declaration
public virtual Task<T> SaveAsync(T saveThis)
Parameters
Type Name Description
T saveThis

The item to save

Returns
Type Description
Task<T>
Exceptions
Type Condition
InvalidOperationException
| Edit this page View Source

SaveBatchAsync(int, int, T[])

Declaration
protected virtual Task<TransactionalBatchResponse> SaveBatchAsync(int batchCount, int currentBatch, T[] batch)
Parameters
Type Name Description
int batchCount
int currentBatch
T[] batch
Returns
Type Description
Task<TransactionalBatchResponse>

Implements

IRepository<T>
  • Edit this page
  • View Source
In this article
Back to top Copyright © www.benday.com | info@benday.com