Filtering.Net.Generator
0.1.2
dotnet add package Filtering.Net.Generator --version 0.1.2
NuGet\Install-Package Filtering.Net.Generator -Version 0.1.2
<PackageReference Include="Filtering.Net.Generator" Version="0.1.2"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
<PackageVersion Include="Filtering.Net.Generator" Version="0.1.2" />
<PackageReference Include="Filtering.Net.Generator"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add Filtering.Net.Generator --version 0.1.2
#r "nuget: Filtering.Net.Generator, 0.1.2"
#:package Filtering.Net.Generator@0.1.2
#addin nuget:?package=Filtering.Net.Generator&version=0.1.2
#tool nuget:?package=Filtering.Net.Generator&version=0.1.2
Filtering.Net.Generator
Roslyn incremental source generator + 24-rule analyzer for Filtering.Net. Emits typed IFilterDefinition<T> implementations and a DI extension at compile time. Catches translatable-method mistakes before EF Core sees them.
This package is analyzer-only — it has no runtime DLL. Install it alongside Filtering.Net.
What it solves
Hand-rolling IFilterDefinition<T> per filter shape is repetitive and error-prone — and the moment you reach for runtime expression construction, EF Core's translatability rules become a runtime surprise. This generator turns a declarative [GenerateFilter<T>] partial into a fully-typed predicate set, and the bundled analyzer rejects shapes that would fail at runtime (e.g., a string operator wired to a numeric column, or a custom operator that calls a non-translatable method).
Install
dotnet add package Filtering.Net
dotnet add package Filtering.Net.Generator
Both are required: Filtering.Net ships the runtime types the generator's emitted code references; Filtering.Net.Generator ships the generator + analyzer.
Quickstart
Declare a partial filter class:
public sealed class User
{
public int Id { get; set; }
public string Name { get; set; } = "";
public int Age { get; set; }
public bool IsActive { get; set; }
}
[GenerateFilter<User>]
public partial class UserFilter
{
[Map(nameof(User.Id), Sortable = true)] private static partial void MapId();
[Map(nameof(User.Name), Sortable = true)] private static partial void MapName();
[Map(nameof(User.Age), Sortable = true)] private static partial void MapAge();
[Map(nameof(User.IsActive))] private static partial void MapIsActive();
}
The generator emits:
UserFilter : IFilterDefinition<User>withValidate(...),ApplyFilter(...),ApplySorting(...)already implemented.- A
services.AddFiltering()extension method (whenMicrosoft.Extensions.DependencyInjection.Abstractionsis referenced) that registers every generated filter class asIFilterDefinition<T>.
Wire it up:
builder.Services.AddDbContext<AppDbContext>(o => o.UseNpgsql(connectionString));
builder.Services.AddFiltering(); // <- emitted by the generator
Custom operators
A profile is a static class decorated with [FilterProfile<T>] that defines a set of operators. Built-in profiles cover common types; declare your own to add custom logic:
[FilterProfile<string>]
public static class StringFilterPlus
{
[BasedOn(typeof(StringFilter))]
private static void Inherit() { }
[FilterOperator("startsWithFold")]
public static Expression<Func<string, string, bool>> StartsWithFold() =>
(column, value) => EF.Functions.ILike(column, value + "%");
}
Then [Map(nameof(User.Name), Profile = typeof(StringFilterPlus))] and the new operator is available on that property.
Diagnostics
24 rules total: 16 errors (FN0001–FN0016) and 8 warnings (FN1001–FN1008). The full catalogue with one-line summaries lives at the diagnostics catalogue on the docs site.
| Id | Severity | Summary |
|---|---|---|
| FN0001 | Error | Property is mapped by multiple [Map] methods — each property must have at most one [Map] declaration. |
| FN0002 | Error | Property is marked Sortable = true on multiple [Map] methods. |
| FN0003 | Error | Property has both a [Map] and a [PropertyMap] — use one or the other. |
| FN0004 | Error | Property referenced by [Map] or [PropertyMap] does not exist on the entity type. |
| FN0005 | Error | Profile cannot be applied to the property — the profile's column type is incompatible with the property's CLR type. |
| FN0006 | Error | Operator referenced in For(...).Operator(...) is not declared by the resolved profile. |
| FN0007 | Error | [Map] method is not declared partial — the generator can only emit implementations for partial methods. |
| FN0008 | Error | Property's CLR type has no built-in primitive profile; specify Profile = typeof(...) explicitly. |
| FN0009 | Error | Property has multiple [InterceptValue] declarations. |
| FN0010 | Error | [FilterOperator] member is not public static. |
| FN0011 | Error | Alias collides with another property or alias on the entity (case-insensitive). |
| FN0012 | Error | [FilterProfile(BasedOn = typeof(...))] references a type that is not marked with [FilterProfile]. |
| FN0013 | Error | Property has [InterceptValue] but no matching [Map] declaration. |
| FN0014 | Error | Property's CLR type is matched by multiple profiles — use Profile = typeof(...) on the [Map] to pick one. |
| FN0015 | Error | Standalone profile has no BasedOn and is missing required extractor method(s). |
| FN0016 | Error | Same operator name declared more than once on a single profile. |
| FN1001 | Warning | [FilterOperator] body references DateTime.UtcNow/Now directly inside the lambda. |
| FN1002 | Warning | Property is mapped but not marked Sortable = true — likely omission for a sortable type. |
| FN1003 | Warning | Profile is declared but never referenced by any [Map(..., Profile = ...)]. |
| FN1004 | Warning | Operator is declared on a profile but never referenced. |
| FN1005 | Warning | Property allows zero operators — Only/Except excluded everything; filter leaves on this field will always fail validation. |
| FN1006 | Warning | Mapped path crosses a nullable navigation property. |
| FN1007 | Warning | [FilterOperator] body calls a method not in the EF Core translatable allow-list — may produce client-side evaluation or runtime errors. |
| FN1008 | Warning | Filter value type is not registered in any visible JsonSerializerContext (opt-in via [assembly: FilterValueDiagnostics(WarnUnregistered = true)]). |
Every rule's helpLinkUri points back at the catalogue table on the docs site.
Editor / build integration
The generator integrates with Roslyn directly — no MSBuild wiring required beyond installing the NuGet. Diagnostics surface in Visual Studio, Rider, VS Code (via OmniSharp / C# Dev Kit), and dotnet build.
See also
- Documentation site — full guides, API reference, diagnostics catalogue.
- Repo on GitHub — source, issue tracker, contribution notes.
Filtering.Net— runtime types the emitted code references.Filtering.Net.EntityFrameworkCore— asyncApplyPagedAsync+PageResult<T>.
License
MIT.
Learn more about Target Frameworks and .NET Standard.
This package has 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.