如何结合BinaryExpression
和Expression
?
例如:
void AddGlobalFilter(Expression > expr) { var parameter = Expression.Parameter(type, "t"); var member = Expression.Property(filter.Parameter, field); var cOnstant= Expression.Constant(null); var body = Expression.Equal(member, constant); var combine = Expression.AndAlso(body, expr); }
我正在尝试为实体框架(EF)核心定义全局过滤器。问题是我必须手动组合多个过滤器。
ModelBuilder
如果模型实现IDbDeleted
接口,则可以添加一个过滤器。
可以为特定模型手动添加另一个。基本思想是我拥有所有表达式的列表,然后将它们组合:
var expression = listExpressions.First(); foreach (var second in listExpressions.Skip(1)) { expression = Expression.AndAlso(expression, second); } var lambdaExpression = Expression.Lambda(expression, parameter); modelBuilder.Entity(item.Key).HasQueryFilter(lambdaExpression);
当然我得到了错误(第一个来自Expression.Equal
,第二个来自t => t...
):
过滤器表达式't => t =>(Not(t。...
编辑:代码看起来像这样:
[Table("MyEntities")] public class DbMyEntity : IDeleted { public string Name { get; set; } public DateTime? DateTimeDeleted { get; set; } } public interface IDeleted { DateTime? DateTimeDeleted { get; set; } } public class MyContext : IdentityDbContext { private Dictionary> dict = new Dictionary >(); private Dictionary dictParameter = new Dictionary (); private ParameterExpression GetParameter(Type type) { if (!this.dictParameter.ContainsKey(type)) { this.dictParameter.Add(type, Expression.Parameter(type, "t")); } return this.dictParameter[type]; } private void AddToDict(Type type, Expression expr) { if (!this.dict.ContainsKey(type)) { this.dict.Add(type, new List ()); this.GetParameter(type); //Just to create ParameterExpression if not exists. } this.dict[type].Add(expr); } private void AddToDict (Expression > expr) { this.AddToDict(typeof(T), expr); } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); foreach (var entity in modelBuilder.Model.GetEntityTypes()) { if (typeof(IDeleted).IsAssignableFrom(entity.ClrType)) { var member = Expression.Property(this.GetParameter(entity.ClrType), "DateTimeDeleted"); var cOnstant= Expression.Constant(null); var body = Expression.Equal(member, constant); this.AddToDict(entity.ClrType, body); } } //This is done in another project in same solution. See comment bellow. this.AddToDict (t => t.Name == null || t.Name == "Something"); //foreach (var builderType in allDllModules) //{ // if (builderType != null && builderType != typeof(ICustomModelBuilder)) // { // var builder = (ICustomModelBuilder)Activator.CreateInstance(builderType); // builder.Build(modelBuilder); // } //} foreach (var item in this.dict) { var expression = item.Value.First(); foreach (var second in item.Value.Skip(1)) { expression = Expression.AndAlso(expression, second); } var lambdaExpression = Expression.Lambda(expression, this.dictParameter[item.Key]); modelBuilder.Entity(item.Key).HasQueryFilter(lambdaExpression); } } }
Ivan Stoev.. 5
您正在将表达式与lambda表达式混合使用。有许多文章显示了如何组合lambda表达式,但是必不可少的部分是从lambda表达式主体组成表达式并重新绑定参数。
后者通常是通过这样的自定义实现的ExpressionVisitor
:
using System.Linq.Expressions; public static class ExpressionExtensions { public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target) { return new ParameterReplacer { Source = source, Target = target }.Visit(expression); } class ParameterReplacer : ExpressionVisitor { public ParameterExpression Source; public Expression Target; protected override Expression VisitParameter(ParameterExpression node) { return node == Source ? Target : base.VisitParameter(node); } } }
现在有关EF Core组合查询过滤器。
对于正在执行的操作,使用字典和表达式列表似乎过于复杂。由于IMutableEntityType
提供了对的读/写访问权限QueryFilter
,因此可以使用一小组自定义扩展方法来实现相同的目的。
他们所有进入这样的类:
using System; using System.Linq.Expressions; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Builders; public static class QueryFilterExtensions { }
第一种方法:
public static void AddQueryFilter(this IMutableEntityType target, LambdaExpression filter) { if (target.QueryFilter == null) target.QueryFilter = filter; else { var parameter = target.QueryFilter.Parameters[0]; var left = target.QueryFilter.Body; var right = filter.Body.ReplaceParameter(filter.Parameters[0], parameter); var body = Expression.AndAlso(left, right); target.QueryFilter = Expression.Lambda(body, parameter); } }
这是一种非通用方法,该方法使用AndAlso
(C#&&
)运算符将现有过滤器与通过的过滤器组合在一起,并显示了上述的lambda表达式组合原理。
但是,它并不是直接有用的,就像在实体类型配置循环内部一样(它可以,但是需要您手动构建lambda表达式,而不是让C#编译器执行此操作)。所以这是第二种方法:
public static void AddQueryFilter(this IMutableEntityType target, Expression > filter) { LambdaExpression targetFilter = filter; if (target.ClrType != typeof(T)) { var parameter = Expression.Parameter(target.ClrType, "e"); var body = filter.Body.ReplaceParameter(filter.Parameters[0], parameter); targetFilter = Expression.Lambda(body, parameter); } target.AddQueryFilter(targetFilter); }
这是一种通用方法-不太安全,但是允许您使用编译时lambda表达式并将其绑定到实际的实体类型,如下所示:
foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { if (typeof(IDeleted).IsAssignableFrom(entityType.ClrType)) entityType.AddQueryFilter(e => e.DateTimeDeleted == null); }
看起来更好,不是吗:)
最后一个自定义扩展方法是对标准EF Core通用HasQueryFilter
方法的补充(替代):
public static EntityTypeBuilderAddQueryFilter (this EntityTypeBuilder target, Expression > filter) where TEntity : class { target.Metadata.AddQueryFilter(filter); return target; }
并允许您更换
this.AddToDict(t => t.Name == null || t.Name == "Something");
更方便
modelBuilder.Entity() .AddQueryFilter(t => t.Name == null || t.Name == "Something");
更新(EF Core 3.0): QueryFilter
属性已替换为GetQueryFilter
和SetQueryFilter
扩展方法。
您正在将表达式与lambda表达式混合使用。有许多文章显示了如何组合lambda表达式,但是必不可少的部分是从lambda表达式主体组成表达式并重新绑定参数。
后者通常是通过这样的自定义实现的ExpressionVisitor
:
using System.Linq.Expressions; public static class ExpressionExtensions { public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target) { return new ParameterReplacer { Source = source, Target = target }.Visit(expression); } class ParameterReplacer : ExpressionVisitor { public ParameterExpression Source; public Expression Target; protected override Expression VisitParameter(ParameterExpression node) { return node == Source ? Target : base.VisitParameter(node); } } }
现在有关EF Core组合查询过滤器。
对于正在执行的操作,使用字典和表达式列表似乎过于复杂。由于IMutableEntityType
提供了对的读/写访问权限QueryFilter
,因此可以使用一小组自定义扩展方法来实现相同的目的。
他们所有进入这样的类:
using System; using System.Linq.Expressions; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Builders; public static class QueryFilterExtensions { }
第一种方法:
public static void AddQueryFilter(this IMutableEntityType target, LambdaExpression filter) { if (target.QueryFilter == null) target.QueryFilter = filter; else { var parameter = target.QueryFilter.Parameters[0]; var left = target.QueryFilter.Body; var right = filter.Body.ReplaceParameter(filter.Parameters[0], parameter); var body = Expression.AndAlso(left, right); target.QueryFilter = Expression.Lambda(body, parameter); } }
这是一种非通用方法,该方法使用AndAlso
(C#&&
)运算符将现有过滤器与通过的过滤器组合在一起,并显示了上述的lambda表达式组合原理。
但是,它并不是直接有用的,就像在实体类型配置循环内部一样(它可以,但是需要您手动构建lambda表达式,而不是让C#编译器执行此操作)。所以这是第二种方法:
public static void AddQueryFilter(this IMutableEntityType target, Expression > filter) { LambdaExpression targetFilter = filter; if (target.ClrType != typeof(T)) { var parameter = Expression.Parameter(target.ClrType, "e"); var body = filter.Body.ReplaceParameter(filter.Parameters[0], parameter); targetFilter = Expression.Lambda(body, parameter); } target.AddQueryFilter(targetFilter); }
这是一种通用方法-不太安全,但是允许您使用编译时lambda表达式并将其绑定到实际的实体类型,如下所示:
foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { if (typeof(IDeleted).IsAssignableFrom(entityType.ClrType)) entityType.AddQueryFilter(e => e.DateTimeDeleted == null); }
看起来更好,不是吗:)
最后一个自定义扩展方法是对标准EF Core通用HasQueryFilter
方法的补充(替代):
public static EntityTypeBuilderAddQueryFilter (this EntityTypeBuilder target, Expression > filter) where TEntity : class { target.Metadata.AddQueryFilter(filter); return target; }
并允许您更换
this.AddToDict(t => t.Name == null || t.Name == "Something");
更方便
modelBuilder.Entity() .AddQueryFilter(t => t.Name == null || t.Name == "Something");
更新(EF Core 3.0): QueryFilter
属性已替换为GetQueryFilter
和SetQueryFilter
扩展方法。