HasQueryFilter not a generic EntityTypeBuilder (unlike a generic EntityTypeBuilder<TEntity> ) is almost unusable because there is no easy way to create the expected LambdaExpression .
One solution is to build the lambda expression manually using the methods of the Expression class:
.ForEach(entityType => { builder.Entity(entityType.ClrType).Property<Boolean>("IsDeleted"); var parameter = Expression.Parameter(entityType.ClrType, "e"); var body = Expression.Equal( Expression.Call(typeof(EF), nameof(EF.Property), new[] { typeof(bool) }, parameter, Expression.Constant("IsDeleted")), Expression.Constant(false)); builder.Entity(entityType.ClrType).HasQueryFilter(Expression.Lambda(body, parameter)); });
Another is to use a prototype expression and use a replacement parameter to bind the parameter to the actual type:
.ForEach(entityType => { builder.Entity(entityType.ClrType).Property<Boolean>("IsDeleted"); var parameter = Expression.Parameter(entityType.ClrType, "e"); var body = filter.Body.ReplaceParameter(filter.Parameters[0], parameter); builder.Entity(entityType.ClrType).HasQueryFilter(Expression.Lambda(body, parameter)); });
where ReplaceParameter is one of the helper helper extension methods that I use to process the expression tree:
public static partial class ExpressionUtils { public static Expression ReplaceParameter(this Expression expr, ParameterExpression source, Expression target) => new ParameterReplacer { Source = source, Target = target }.Visit(expr); class ParameterReplacer : System.Linq.Expressions.ExpressionVisitor { public ParameterExpression Source; public Expression Target; protected override Expression VisitParameter(ParameterExpression node) => node == Source ? Target : node; } }
But the most natural solution, in my opinion, is to transfer the configuration code to a general method and call it through reflection. For instance:
static void ConfigureSoftDelete<T>(ModelBuilder builder) where T : class, IDeletableEntity { builder.Entity<T>().Property<Boolean>("IsDeleted"); builder.Entity<T>().HasQueryFilter(e => EF.Property<bool>(e, "IsDeleted") == false); }
and then
.ForEach(entityType => GetType() .GetMethod(nameof(ConfigureSoftDelete), BindingFlags.NonPublic | BindingFlags.Static) .MakeGenericMethod(entityType.ClrType) .Invoke(null, new object[] { builder }) );