- Add path-specific rules for commands/queries, dynamic queries, validation, and gRPC - Add /add-command, /add-query, /add-dynamic-query scaffolding skills - Add project settings with post-edit formatting, proto validation, and build-gate hooks - Add .editorconfig codifying existing code style conventions - Trim CLAUDE.md from 414 to 130 lines (domain details moved to rules) - Add .harness-version tracking for the shared claude-harness repo Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3.9 KiB
| name | description | argument-hint |
|---|---|---|
| add-dynamic-query | Scaffold a dynamic query with IQueryableProviderOverride for automatic pagination, filtering, and sorting. This is the DEFAULT choice for any list/collection query. | <entity name and optional description> |
Add Dynamic Query
Scaffold a dynamic query based on: $ARGUMENTS
This is the default query pattern. It gives pagination, filtering, sorting, grouping, and aggregation for free.
Instructions
1. Identify the entity
Determine the source entity type from the user's description. If the project uses a DAL with EF Core, the entity should already exist in the DbContext.
2. Determine the feature folder
Place the file in: Features/{DomainArea}/
3. Create the queryable provider
Create Features/{DomainArea}/{Entity}QueryableProvider.cs:
using Svrnty.CQRS.DynamicQuery.Abstractions;
namespace {ProjectNamespace}.Features.{DomainArea};
public class {Entity}QueryableProvider : IQueryableProviderOverride<{Entity}>
{
private readonly {DbContextType} _db;
public {Entity}QueryableProvider({DbContextType} db) => _db = db;
public Task<IQueryable<{Entity}>> GetQueryableAsync(object query, CancellationToken cancellationToken = default)
=> Task.FromResult(_db.{EntityPlural}.AsQueryable());
}
If the entity needs a DTO projection (different shape for the API), note this for the registration step.
4. Create alter queryable service (if needed)
If the entity needs security filtering, tenant isolation, or default ordering, create Features/{DomainArea}/{Entity}AlterQueryable.cs:
using Svrnty.CQRS.DynamicQuery.Abstractions;
namespace {ProjectNamespace}.Features.{DomainArea};
public class {Entity}AlterQueryable : IAlterQueryableService<{Entity}, {EntityOrDto}>
{
public Task<IQueryable<{Entity}>> AlterQueryableAsync(
IQueryable<{Entity}> query,
IDynamicQuery dynamicQuery,
CancellationToken cancellationToken = default)
{
// Add default ordering, security filters, etc.
return Task.FromResult(query.OrderByDescending(x => x.CreatedAt));
}
}
5. Register in DI
Find the project's service registration and add:
// Basic — entity returned as-is
builder.Services.AddDynamicQueryWithProvider<{Entity}, {Entity}QueryableProvider>();
// With DTO projection — entity mapped to DTO
builder.Services.AddDynamicQueryWithProvider<{Entity}, {EntityDto}, {Entity}QueryableProvider>();
// With alter service (add after provider registration)
builder.Services.AddAlterQueryable<{Entity}, {EntityOrDto}, {Entity}AlterQueryable>();
6. Ensure dynamic query dependencies are registered
Check that these are registered (typically once per project, before AddSvrntyCqrs):
builder.Services.AddTransient<IAsyncQueryableService, SimpleAsyncQueryableService>();
builder.Services.AddTransient<IQueryHandlerAsync, QueryHandlerAsync>();
If they're already present, don't add duplicates.
7. Proto message (if project uses gRPC)
// In the DynamicQueryService definition
rpc Query{EntityPlural} (DynamicQuery{EntityPlural}Request) returns (DynamicQuery{EntityPlural}Response);
// Entity message (if not already defined)
message {Entity} {
// fields matching entity properties
}
// Request — uses standard dynamic query fields
message DynamicQuery{EntityPlural}Request {
int32 page = 1;
int32 page_size = 2;
repeated DynamicQueryFilter filters = 3;
repeated DynamicQuerySort sorts = 4;
repeated DynamicQueryGroup groups = 5;
repeated DynamicQueryAggregate aggregates = 6;
}
// Response
message DynamicQuery{EntityPlural}Response {
repeated {Entity} data = 1;
int64 total_records = 2;
int32 number_of_pages = 3;
}
The standard DynamicQueryFilter, DynamicQuerySort, DynamicQueryGroup, and DynamicQueryAggregate messages should already be defined — reuse them.
8. Summary
After creating everything, list what was created and where.