📖 Documentation site · NuGet · Changelog
Compile-time query composition for IQueryable<T> via Roslyn incremental source generators.
Add [QuerySpec(typeof(Product))] to a partial query class and AutoQuery.Generator emits strongly-typed Apply(IQueryable<Product>), BindFromQuery(...), and FromQuery(...) methods at build time. No reflection. No runtime scanning. AOT-friendly.
usingAutoQuery;usingSystem.Linq;[QuerySpec(typeof(Product))]publicpartialclassProductQuery{publicstring?Name{get;set;}publicdecimal?MinPrice{get;set;}publicdecimal?MaxPrice{get;set;}[QuerySort]publicstring?SortBy{get;set;}publicboolSortDescending{get;set;}publicintPageNumber{get;set;}=1;publicintPageSize{get;set;}=20;}varfiltered=newProductQuery{Name="Laptop",MinPrice=500,SortBy="Name",PageNumber=1,PageSize=10,}.Apply(products);// Or bind directly from HTTP query-string values:varrequestQuery=newDictionary<string,string?>{["Name"]="Laptop",["MinPrice"]="500",["SortBy"]="Name",["PageNumber"]="1",["PageSize"]="10"};varbound=ProductQuery.FromQuery(requestQuery);varfilteredFromQuery=bound.Apply(products);dotnet add package AutoQuery.GeneratorTargets netstandard2.0 and works with modern SDK-style .NET projects.
usingAutoQuery;publicsealedclassProduct{publicstring?Name{get;set;}publicdecimalPrice{get;set;}publicboolIsActive{get;set;}}[QuerySpec(typeof(Product))]publicpartialclassProductQuery{publicstring?Name{get;set;}publicdecimal?MinPrice{get;set;}publicbool?IsActive{get;set;}}IQueryable<Product>query=dbContext.Products;varspec=newProductQuery{Name="Phone",MinPrice=100,IsActive=true};varresult=spec.Apply(query);usingAutoQuery;[QuerySpec(typeof(Product))]publicpartialclassProductQuery{publicstring?Name{get;set;}publicdecimal?MinPrice{get;set;}[QuerySort]publicstring?SortBy{get;set;}publicboolSortDescending{get;set;}publicintPageNumber{get;set;}=1;publicintPageSize{get;set;}=20;}// Works with Dictionary<string, string?>varquery=ProductQuery.FromQuery(newDictionary<string,string?>{["Name"]="Laptop",["MinPrice"]="500",["SortBy"]="Name",["PageNumber"]="1",["PageSize"]="10"});// Also works with ASP.NET Core Request.Query without taking an ASP.NET Core package dependency.app.MapGet("/products",(AppDbContextdb,HttpRequestrequest)=>{varspec=ProductQuery.FromQuery(request.Query);returnspec.Apply(db.Products);});Unknown keys are ignored, malformed values are skipped, and successful conversions use invariant culture for numeric/date parsing plus case-insensitive enum parsing.
Generated output resembles:
publicpartialclassProductQuery{publicIQueryable<global::YourApp.Product>Apply(IQueryable<global::YourApp.Product>query){if(Nameis not null)query=query.Where(x =>x.Name!=null&&x.Name.Contains(Name));if(MinPriceis not null)query=query.Where(x =>x.Price>=MinPrice.Value);if(IsActiveis not null)query=query.Where(x =>x.IsActive==IsActive.Value);returnquery;}}Marks a partial class as a query spec for the target entity.
[QuerySpec(typeof(Order))]publicpartialclassOrderQuery{}Overrides the default convention and uses your custom LINQ predicate expression. The token value is replaced with the query property access.
[QueryFilter("x => x.Category.Name == value")]publicstring?CategoryName{get;set;}Skips a property entirely.
[QueryIgnore]publicstring?DebugOnly{get;set;}Marks a string? property as the requested sort field.
[QuerySort]publicstring?SortBy{get;set;}publicboolSortDescending{get;set;}Marks pagination properties when you are not using the conventional names PageNumber and PageSize.
[QueryPage]publicintResultsPageNumber{get;set;}=1;[QueryPage]publicintResultsPageSize{get;set;}=25;Nullable properties become filters automatically unless excluded.
| Spec property | Generated predicate |
|---|---|
string? Name | x => x.Name != null && x.Name.Contains(Name) |
bool? IsActive | x => x.IsActive == IsActive.Value |
int? CategoryId | x => x.CategoryId == CategoryId.Value |
decimal? MinPrice | x => x.Price >= MinPrice.Value |
decimal? MaxPrice | x => x.Price <= MaxPrice.Value |
DateTime? CreatedFrom | x => x.Created >= CreatedFrom.Value |
DateTime? CreatedTo | x => x.Created <= CreatedTo.Value |
Prefix/suffix conventions:
Min...→>=Max...→<=...From→>=...To→<=
When the spec contains a [QuerySort] string property and a bool property named SortDescending or IsDescending, AutoQuery emits switch-based sorting.
[QuerySpec(typeof(Product))]publicpartialclassProductQuery{publicstring?Name{get;set;}publicdecimal?Price{get;set;}[QuerySort]publicstring?SortBy{get;set;}publicboolSortDescending{get;set;}}Generated shape:
if(SortByis not null){query=(SortBy,SortDescending)switch{("Name",false)=>query.OrderBy(x =>x.Name),("Name",true)=>query.OrderByDescending(x =>x.Name),("Price",false)=>query.OrderBy(x =>x.Price),("Price",true)=>query.OrderByDescending(x =>x.Price),
_ =>query};}When PageNumber and PageSize are present (or [QueryPage] annotated equivalents are detected), AutoQuery emits:
query=query.Skip((PageNumber-1)*PageSize).Take(PageSize);Example:
[QuerySpec(typeof(Product))]publicpartialclassProductQuery{publicstring?Name{get;set;}publicintPageNumber{get;set;}=1;publicintPageSize{get;set;}=20;}For every [QuerySpec] class with writable supported properties, AutoQuery emits:
publicvoidBindFromQuery(IEnumerable<KeyValuePair<string,string?>>query);publicvoidBindFromQuery<TValue>(IEnumerable<KeyValuePair<string,TValue>>query)whereTValue:IEnumerable<string>;publicstaticProductQueryFromQuery(IEnumerable<KeyValuePair<string,string?>>query);publicstaticProductQueryFromQuery<TValue>(IEnumerable<KeyValuePair<string,TValue>>query)whereTValue:IEnumerable<string>;Supported property conversions:
string- numeric types and nullable numeric types
bool/bool?DateTime/DateTime?- enums and nullable enums
The generic overload is what lets Request.Query bind cleanly: IQueryCollection enumerates as KeyValuePair<string, StringValues>, and StringValues implements IEnumerable<string>, so no AutoQuery runtime dependency on ASP.NET Core is required.
| Id | Severity | Description |
|---|---|---|
AQ001 | Error | [QuerySpec] entity type could not be resolved. |
AQ002 | Error | [QuerySpec] target class must be declared partial. |
AQ003 | Warning | Spec has no filterable properties. |
| Capability | AutoQuery.Generator | Manual LINQ | Ardalis.Specification |
|---|---|---|---|
Compile-time generated Apply method | ✅ | ❌ | ❌ |
| Compile-time generated query-string binding | ✅ | ❌ | ❌ |
| Reflection-free | ✅ | ✅ | Usually ✅ |
| Convention filters from DTO-like class | ✅ | ❌ | ❌ |
| Custom inline filter expressions | ✅ | Manual only | Via handwritten spec logic |
| Built-in sort switch generation | ✅ | Manual only | Manual only |
| Built-in pagination generation | ✅ | Manual only | Manual only |
| Runtime abstraction dependency | None | None | Package dependency |
🌐 Full suite overview: swevo.github.io
| Package | Description |
|---|---|
| AutoWire | Compile-time DI auto-registration — [Scoped]/[Singleton]/[Transient] generates IServiceCollection code. Zero reflection. |
| AutoMap.Generator | Compile-time object mapping — [Map(typeof(Dto))] generates ToDto() extension methods. Zero reflection, AOT-safe. |
| AutoValidate.Generator | Compile-time FluentValidation wiring — discovers AbstractValidator<T> subclasses and generates AddValidators(). |
| AutoResult.Generator | Compile-time Result<T> monad — [TryWrap] generates Try*() wrappers for sync, async and void methods. |
| AutoDispatch.Generator | Compile-time CQRS dispatcher — [Handler] generates a strongly-typed IDispatcher. No IRequest<T>, no reflection. |
| AutoLog.Generator | Compile-time high-performance logging — [Log(Level, Message)] on a partial method generates LoggerMessage.Define. AOT-safe. |
| AutoHttpClient.Generator | Compile-time typed HTTP client — [HttpClient] on an interface generates a strongly-typed client. AOT-safe Refit alternative. |
| Package | Downloads | Description |
|---|---|---|
| AutoWire | Compile-time dependency injection auto-registration for | |
| AutoMap.Generator | Compile-time object mapping for | |
| AutoArchitecture | Compile-time architecture/dependency-rule enforcement for | |
| AutoHttpClient.Generator | Compile-time typed HTTP client generation for | |
| AutoDispatch.Generator | Compile-time CQRS dispatcher for | |
| AutoLog.Generator | Compile-time high-performance logging for | |
| AutoValidate.Generator | Compile-time FluentValidation wiring for |
MIT.