AutoQuery.Generator
1.0.1
See the version list below for details.
dotnet add package AutoQuery.Generator --version 1.0.1
NuGet\Install-Package AutoQuery.Generator -Version 1.0.1
<PackageReference Include="AutoQuery.Generator" Version="1.0.1" />
<PackageVersion Include="AutoQuery.Generator" Version="1.0.1" />
<PackageReference Include="AutoQuery.Generator" />
paket add AutoQuery.Generator --version 1.0.1
#r "nuget: AutoQuery.Generator, 1.0.1"
#:package AutoQuery.Generator@1.0.1
#addin nuget:?package=AutoQuery.Generator&version=1.0.1
#tool nuget:?package=AutoQuery.Generator&version=1.0.1
AutoQuery.Generator
๐ 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 a strongly-typed Apply(IQueryable<Product>) method at build time. No reflection. No runtime scanning. AOT-friendly.
using AutoQuery;
using System.Linq;
[QuerySpec(typeof(Product))]
public partial class ProductQuery
{
public string? Name { get; set; }
public decimal? MinPrice { get; set; }
public decimal? MaxPrice { get; set; }
[QuerySort] public string? SortBy { get; set; }
public bool SortDescending { get; set; }
public int PageNumber { get; set; } = 1;
public int PageSize { get; set; } = 20;
}
var filtered = new ProductQuery
{
Name = "Laptop",
MinPrice = 500,
SortBy = "Name",
PageNumber = 1,
PageSize = 10,
}.Apply(products);
Installation
dotnet add package AutoQuery.Generator
Targets netstandard2.0 and works with modern SDK-style .NET projects.
Quick start
1. Define an entity and query spec
using AutoQuery;
public sealed class Product
{
public string? Name { get; set; }
public decimal Price { get; set; }
public bool IsActive { get; set; }
}
[QuerySpec(typeof(Product))]
public partial class ProductQuery
{
public string? Name { get; set; }
public decimal? MinPrice { get; set; }
public bool? IsActive { get; set; }
}
2. Use the generated Apply method
IQueryable<Product> query = dbContext.Products;
var spec = new ProductQuery { Name = "Phone", MinPrice = 100, IsActive = true };
var result = spec.Apply(query);
Generated output resembles:
public partial class ProductQuery
{
public IQueryable<global::YourApp.Product> Apply(IQueryable<global::YourApp.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;
}
}
Attributes
[QuerySpec(typeof(TEntity))]
Marks a partial class as a query spec for the target entity.
[QuerySpec(typeof(Order))]
public partial class OrderQuery { }
[QueryFilter("x => x.Category.Name == value")]
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")]
public string? CategoryName { get; set; }
[QueryIgnore]
Skips a property entirely.
[QueryIgnore]
public string? DebugOnly { get; set; }
[QuerySort]
Marks a string? property as the requested sort field.
[QuerySort]
public string? SortBy { get; set; }
public bool SortDescending { get; set; }
[QueryPage]
Marks pagination properties when you are not using the conventional names PageNumber and PageSize.
[QueryPage] public int ResultsPageNumber { get; set; } = 1;
[QueryPage] public int ResultsPageSize { get; set; } = 25;
Convention-based filters
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โ<=
Sorting
When the spec contains a [QuerySort] string property and a bool property named SortDescending or IsDescending, AutoQuery emits switch-based sorting.
[QuerySpec(typeof(Product))]
public partial class ProductQuery
{
public string? Name { get; set; }
public decimal? Price { get; set; }
[QuerySort] public string? SortBy { get; set; }
public bool SortDescending { get; set; }
}
Generated shape:
if (SortBy is 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
};
}
Pagination
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))]
public partial class ProductQuery
{
public string? Name { get; set; }
public int PageNumber { get; set; } = 1;
public int PageSize { get; set; } = 20;
}
Diagnostics
| 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. |
Comparison
| Capability | AutoQuery.Generator | Manual LINQ | Ardalis.Specification |
|---|---|---|---|
Compile-time generated Apply method |
โ | โ | โ |
| 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 |
Also by the same author
๐ 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. |
License
MIT.
Learn more about Target Frameworks and .NET Standard.
-
.NETStandard 2.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.