Cosmos DB repository pattern with LINQ queries, FeedIterator for pagination, point reads, and RU monitoring. Covers container-based data access patterns. Trigger: cosmos repository, LINQ query, FeedIterator, point read, RU.
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.
Transforms Claude Code into autonomous agent system with persistent memory, scheduled operations, computer use, and task queuing. Use for continuous operation, scheduled tasks, or self-directing agent loops.
ReadItemAsync) are the most efficient operationGetItemLinqQueryable<T>() with discriminator filteringFeedIterator for paginating large result setsnamespace {Company}.{Domain}.Cosmos.Infrastructure;
public sealed class CosmosRepository<T>(
Container container,
ILogger<CosmosRepository<T>> logger) where T : IContainerDocument
{
public async Task<T?> GetByIdAsync(
string id, PartitionKey pk, CancellationToken ct)
{
try
{
var response = await container.ReadItemAsync<T>(
id, pk, cancellationToken: ct);
logger.LogDebug("Point read: {RU} RUs", response.RequestCharge);
return response.Resource;
}
catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
{
return default;
}
}
public async Task<List<T>> QueryAsync(
Expression<Func<T, bool>> predicate,
PartitionKey? pk = null,
CancellationToken ct = default)
{
var options = new QueryRequestOptions();
if (pk.HasValue)
options.PartitionKey = pk.Value;
var queryable = container.GetItemLinqQueryable<T>(
requestOptions: options)
.Where(d => d.Discriminator == typeof(T).Name)
.Where(predicate);
using var iterator = queryable.ToFeedIterator();
var results = new List<T>();
double totalRU = 0;
while (iterator.HasMoreResults)
{
var response = await iterator.ReadNextAsync(ct);
results.AddRange(response);
totalRU += response.RequestCharge;
}
logger.LogDebug("Query returned {Count} items, {RU} RUs",
results.Count, totalRU);
return results;
}
public async Task UpsertAsync(T document, CancellationToken ct)
{
var response = await container.UpsertItemAsync(
document, document.PartitionKeys,
new ItemRequestOptions { IfMatchEtag = document.ETag },
ct);
document.ETag = response.ETag;
logger.LogDebug("Upsert: {RU} RUs", response.RequestCharge);
}
public async Task CreateAsync(T document, CancellationToken ct)
{
var response = await container.CreateItemAsync(
document, document.PartitionKeys, cancellationToken: ct);
document.ETag = response.ETag;
logger.LogDebug("Create: {RU} RUs", response.RequestCharge);
}
public async Task DeleteAsync(
string id, PartitionKey pk, CancellationToken ct)
{
await container.DeleteItemAsync<T>(id, pk, cancellationToken: ct);
}
}
public async Task<(List<T> Items, string? ContinuationToken)> QueryPagedAsync(
Expression<Func<T, bool>> predicate,
int pageSize,
string? continuationToken = null,
CancellationToken ct = default)
{
var queryable = container.GetItemLinqQueryable<T>(
continuationToken: continuationToken,
requestOptions: new QueryRequestOptions { MaxItemCount = pageSize })
.Where(d => d.Discriminator == typeof(T).Name)
.Where(predicate);
using var iterator = queryable.ToFeedIterator();
var results = new List<T>();
string? nextToken = null;
if (iterator.HasMoreResults)
{
var response = await iterator.ReadNextAsync(ct);
results.AddRange(response);
nextToken = response.ContinuationToken;
}
return (results, nextToken);
}
public async Task<List<T>> QuerySqlAsync(
string sql, Dictionary<string, object> parameters, CancellationToken ct)
{
var queryDef = new QueryDefinition(sql);
foreach (var param in parameters)
queryDef = queryDef.WithParameter($"@{param.Key}", param.Value);
using var iterator = container.GetItemQueryIterator<T>(queryDef);
var results = new List<T>();
while (iterator.HasMoreResults)
{
var response = await iterator.ReadNextAsync(ct);
results.AddRange(response);
}
return results;
}
| Anti-Pattern | Correct Approach |
|---|---|
| Missing discriminator filter | Always filter by Discriminator in queries |
| Cross-partition queries for known PK | Provide partition key when available |
| Not tracking RU charges | Log RU charges for monitoring and optimization |
| Using ReadItemAsync without ETag | Set ETag from read for subsequent updates |
| Large unbounded queries | Use FeedIterator with MaxItemCount for pagination |
# Find Cosmos repositories
grep -r "CosmosRepository\|Container container" --include="*.cs" src/
# Find LINQ queries
grep -r "GetItemLinqQueryable" --include="*.cs" src/
# Find FeedIterator usage
grep -r "FeedIterator\|ToFeedIterator" --include="*.cs" src/
# Find RU logging
grep -r "RequestCharge" --include="*.cs" src/
CosmosRepository<T> exists or if repos are per-entityReadItemAsync) wherever possible over queries