HasQueryFilter
неуниверсального EntityTypeBuilder
(в отличие от универсального EntityTypeBuilder<TEntity>
) почти непригоден, потому что нет простого способа создать ожидаемый LambdaExpression
.
Одним из решений является создание лямбда-выражения вручную с использованием методов класса Expression
:
.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));
});
Другой - использовать выражение прототипа
Expression<Func<object, bool>> filter =
e => EF.Property<bool>(e, "IsDeleted") == false;
и используйте заменитель параметра, чтобы связать параметр с фактическим типом:
.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));
});
где ReplaceParameter
— один из пользовательских вспомогательных методов расширения, которые я использую для манипуляции с деревом выражений:
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;
}
}
Но наиболее естественным решением, на мой взгляд, является перемещение кода конфигурации в общий метод и вызов его через отражение. Например:
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);
}
а потом
.ForEach(entityType => GetType()
.GetMethod(nameof(ConfigureSoftDelete), BindingFlags.NonPublic | BindingFlags.Static)
.MakeGenericMethod(entityType.ClrType)
.Invoke(null, new object[] { builder })
);
person
Ivan Stoev
schedule
06.12.2017