--- name: add-dynamic-query description: Scaffold a dynamic query with IQueryableProviderOverride for automatic pagination, filtering, and sorting. This is the DEFAULT choice for any list/collection query. argument-hint: --- # 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`: ```csharp 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> 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`: ```csharp using Svrnty.CQRS.DynamicQuery.Abstractions; namespace {ProjectNamespace}.Features.{DomainArea}; public class {Entity}AlterQueryable : IAlterQueryableService<{Entity}, {EntityOrDto}> { public Task> 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: ```csharp // 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`): ```csharp builder.Services.AddTransient(); builder.Services.AddTransient(); ``` If they're already present, don't add duplicates. ### 7. Proto message (if project uses gRPC) ```protobuf // 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.