Compile-time LINQ query specs for .NET
dotnet add package AutoQuery.Generator
using AutoQuery;
[QuerySpec(typeof(Product))]
public partial class ProductQuery
{
public string? Name { get; set; }
public decimal? MinPrice { get; set; }
public bool? IsActive { get; set; }
}
// ✨ ProductQuery.AutoQuery.g.cs
public partial class ProductQuery
{
public IQueryable<Product> Apply(IQueryable<Product> query)
{
if (Name is not null)
query = query.Where(x => x.Name != null && x.Name.Contains(Name));
if (MinPrice is not null)
query = query.Where(x => x.Price >= MinPrice.Value);
if (IsActive is not null)
query = query.Where(x => x.IsActive == IsActive.Value);
return query;
}
}
Define a partial spec once and let compile-time conventions generate the LINQ for filtering, paging, and sorting.
string? becomes Contains,
nullable value types become equality checks, and range naming like MinPrice
is generated automatically.
Add PageNumber and PageSize
and AutoQuery emits the Skip().Take() pipeline for you.
A [QuerySort] string plus
SortDescending generates a compile-time switch expression for ordering.
Use [QueryFilter("x.Price > MinPrice")] when you want an inline predicate
instead of the default property convention.
All LINQ is emitted at build time. No runtime reflection, no runtime expression building, and no dynamic behavior that breaks AOT.
AQ001, AQ002, and
AQ003 catch generator misuse while you build — not after you deploy.
From simple filters to paging, sorting, and custom predicates.
using AutoQuery;
[QuerySpec(typeof(Product))]
public partial class ProductQuery
{
public string? Name { get; set; } // → .Where(x => x.Name.Contains(Name))
public decimal? MinPrice { get; set; } // → .Where(x => x.MinPrice >= MinPrice)
public bool? IsActive { get; set; } // → .Where(x => x.IsActive == IsActive)
}
// Usage:
var query = new ProductQuery { Name = "Widget", IsActive = true };
var results = await dbContext.Products
.Apply(query)
.ToListAsync();
// Auto-generated by AutoQuery.Generator
public partial class ProductQuery
{
public IQueryable<Product> Apply(IQueryable<Product> source)
{
if (Name is not null)
source = source.Where(x => x.Name.Contains(Name));
if (MinPrice is not null)
source = source.Where(x => x.Price >= MinPrice.Value);
if (IsActive is not null)
source = source.Where(x => x.IsActive == IsActive.Value);
return source;
}
}
[QuerySpec(typeof(Product))]
public partial class ProductQuery
{
public string? Name { get; set; }
[QuerySort]
public string? SortBy { get; set; } // → switch expression on column name
public bool SortDescending { get; set; }
public int PageNumber { get; set; } = 1;
public int PageSize { get; set; } = 20; // → .Skip((PageNumber-1)*PageSize).Take(PageSize)
}
[QuerySpec(typeof(Product))]
public partial class ProductQuery
{
public decimal? MinPrice { get; set; }
public decimal? MaxPrice { get; set; }
[QueryFilter("x.Price >= MinPrice")]
public decimal? PriceFrom { get; set; }
[QueryFilter("x.Price <= MaxPrice")]
public decimal? PriceTo { get; set; }
[QueryIgnore]
public string? InternalNotes { get; set; } // excluded from Apply()
}
Misuse is surfaced by the compiler before your query ever runs.
| Code | Severity | Description |
|---|---|---|
| AQ001 | ⛔ Error | [QuerySpec] applied to a non-partial class |
| AQ002 | ⚠ Warning | [QuerySpec] class has no filterable properties |
| AQ003 | ⚠ Warning | [QueryFilter] expression is blank |
AutoQuery.Generator keeps the simplicity of plain LINQ while removing repetitive boilerplate.
| Feature | AutoQuery.Generator | Manual LINQ | Ardalis.Specification |
|---|---|---|---|
Compile-time generated Apply |
✅ | ❌ | ❌ |
| Convention filters from DTO properties | ✅ | ❌ | ❌ |
| Zero reflection | ✅ | ✅ | Usually ✅ |
| Built-in pagination generation | ✅ | Manual | Manual |
| Built-in sort switch | ✅ | Manual | Manual |
| AOT-safe | ✅ | ✅ | ✅ |
| Runtime abstraction dependency | None | None | Package dependency |
More compile-time tooling for .NET from Justin Bannister.
Explore the full suite of Swevo packages and documentation.
Compile-time dependency injection registration for .NET.
Compile-time object mapping — GitHub · Docs: https://swevo.github.io/AutoMap.Generator/
Compile-time FluentValidation wiring — GitHub · Docs: https://swevo.github.io/AutoValidate.Generator/
Generated result wrappers for cleaner exception-safe application code.
Compile-time command and handler dispatch generation.