EF Core predicate source generator with provider-aware LIKE/ILIKE support.
$ dotnet add package BlogDoFT.Libs.EntityFramework.CodeGeneratorA Roslyn Source Generator that reads filter DTOs annotated with BlogDoFT.Libs.EntityFramework.CodeGenerator.Abstractions.GeneratePredicateAttribute<TEntity> and emits:
ToPredicate() → Expression<Func<TEntity, bool>>HasFilter() → boolThe generated predicates are provider-aware:
Npgsql.EntityFrameworkCore.PostgreSQL is referenced), string filters with % use ILIKE.% use EF.Functions.Like.NpgsqlDbFunctionsExtensions.ILike at compile-time.Target Framework:
netstandard2.0(Analyzer-friendly)
C# Language Version: latest
Install the NuGet package:
dotnet add package BlogDoFT.Libs.EntityFramework.CodeGenerator.Generators
The application must also reference
BlogDoFT.Libs.EntityFramework.CodeGenerator.Abstractions.
[GeneratePredicate<TEntity>];partial; otherwise it reports PG001 (error).StringFilterAttributeNumericFilterAttributeTemporalFilterAttributeBooleanFilterAttributeNpgsqlDbFunctionsExtensions.ILike(DbFunctions, string, string) is available:
HasFilter() and ToPredicate() into the same DTO namespace and type.
namespace Domain;
public class UserRecord
{
public Guid Id { get; set; }
public string Name { get; set; } = "";
public string Email { get; set; } = "";
public int Age { get; set; }
public DateTime CreatedAt { get; set; }
public bool Active { get; set; }
}
using BlogDoFT.Libs.EntityFramework.CodeGenerator.Abstractions;
[GeneratePredicate<Domain.UserRecord>]
public partial class UserFilterDto
{
[StringFilter(TargetProperty = nameof(Domain.UserRecord.Name), Order = 1)]
public string? Name { get; init; }
[StringFilter(TargetProperty = nameof(Domain.UserRecord.Email), Order = 2)]
public string? Email { get; init; }
[NumericFilter(TargetProperty = nameof(Domain.UserRecord.Age), Operator = ComparisonOperator.GreaterThanOrEqual)]
public int? MinimumAge { get; init; }
[TemporalFilter(TargetProperty = nameof(Domain.UserRecord.CreatedAt), Operator = ComparisonOperator.LessThanOrEqual)]
public DateTime? CreatedUntil { get; init; }
[BooleanFilter(TargetProperty = nameof(Domain.UserRecord.Active))]
public bool? Active { get; init; }
}
var filter = new UserFilterDto
{
Name = "%ann%",
MinimumAge = 18,
Active = true
};
var predicate = filter.ToPredicate();
var users = await db.Set<UserRecord>()
.Where(predicate)
.ToListAsync();
%, the generator emits equality (field == value).%, the generator emits a database pattern using:
ILIKE (PostgreSQL with Npgsql)LIKE (fallback on other providers)
This way you keep multi-database compatibility while getting case-insensitive matching on PostgreSQL.If you want to support
*as a wildcard in input, normalize it to%in your DTO before callingToPredicate().
partial;Install and configure console logging:
<PackageReference Include="Microsoft.Extensions.Logging.Console" />
services.AddLogging(b =>
{
b.ClearProviders();
b.AddSimpleConsole();
b.AddFilter("Microsoft.EntityFrameworkCore.Database.Command", LogLevel.Information);
});
Add in your DbContext Options:
options
.UseLoggerFactory(loggerFactory)
.EnableSensitiveDataLogging()
.EnableDetailedErrors();