3

我正在尝试使用 c# 中的表达式来使用实体框架全局过滤器。我可以毫无问题地构建单个或组合表达式的预期表达式。

我使用模型构建器中的属性为所有实体应用了查询过滤器,如下所示

  1. 创建了一个具有属性的虚拟类,将对其应用查询过滤器。(即IsActive)
  2. 使用“VisitMember()”覆盖该虚拟类的表达式以创建新表达式,该表达式实际上将虚拟类的成员转换为实体类型。

注意:我尝试应用简单的查询过滤器,例如“i => i.IsActive == true”。使用“AndAlso”的单个表达式和组合表达式都面临此错误。

经检查,预期的查询过滤器已应用于各个表,没有任何错误。我将从我的 DbContext 调用我的模型构建器扩展方法,如下所示

DbContext 中的代码:

modelBuilder.GlobalFilters<FilterColumns>(i => i.TenantId == 1, nameof(FilterColumns.TenantId));

我的自定义类:

public class FilterColumns
{
   public int TenantId {get; set;}
}

我的模型构建器扩展方法:

    public static void GlobalFilters<TClass>(this ModelBuilder modelBuilder, Expression<Func<TClass, bool>> expression, string property)
    {
        var body = expression?.Body;
        var baseType = expression?.GetType().GetGenericArguments().FirstOrDefault()?.GetGenericArguments().FirstOrDefault();
        var propertyType = baseType?.GetProperty(property)?.PropertyType;

        if (body != null)
        {
            if (property != null)
            {
                var allEntities = modelBuilder?.Model.GetEntityTypes().Where(e => e.FindProperty(property) != null).Select(e => e.ClrType).ToList();

                if (allEntities != null)
                {
                    foreach (var entity in allEntities)
                    {
                        var entityPropertyType = entity?.GetProperty(property)?.PropertyType;

                            if (entityPropertyType == propertyType)
                            {
                                var param = expression?.Parameters.Single().ToString();
                                var expressionVisitor = new CustomExpressionVisitor(entity, baseType, param.ToString());
                                var customExpression = expressionVisitor.Visit(expression?.Body);

                                var lambdaExpression = Expression.Lambda(customExpression, param);

                                modelBuilder?.Entity(entity).HasQueryFilter(lambdaExpression);
                            }                            
                    }
                }
            }
        }
    }

CustomExpressionVisitor 类:

public class CustomExpressionVisitor : ExpressionVisitor
{
    private readonly Type entityType;
    private readonly Type baseEntityType;
    private readonly string param;

    public CustomExpressionVisitor(Type entityType, Type baseEntityType, string param)
    {
        this.entityType = entityType;
        this.baseEntityType = baseEntityType;
        this.param = param;
    }
    protected override Expression VisitMember(MemberExpression expression)
    {
        if (expression.Member.DeclaringType == this.baseEntityType)
        {
            var convertedParam = Expression.Parameter(entityType, this.param);
            return Expression.PropertyOrField(convertedParam, expression.Member.Name);
        }

        return base.VisitMember(expression);
    }
}

我已经通过应用查询过滤器期望从表中得到结果,但是当我尝试使用 linq 查询从表中访问数据时,我收到以下错误。您能提出解决此错误的解决方案吗?

   at System.Linq.Expressions.Compiler.VariableBinder.Reference(ParameterExpression node, VariableStorageKind storage)
   at System.Linq.Expressions.Compiler.VariableBinder.VisitParameter(ParameterExpression node)
   at System.Linq.Expressions.ParameterExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.Compiler.VariableBinder.Visit(Expression node)
   at System.Linq.Expressions.ExpressionVisitor.VisitMember(MemberExpression node)
   at System.Linq.Expressions.MemberExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.Compiler.VariableBinder.Visit(Expression node)
   at System.Linq.Expressions.ExpressionVisitor.VisitBinary(BinaryExpression node)
   at System.Linq.Expressions.BinaryExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.Compiler.VariableBinder.Visit(Expression node)
   at System.Linq.Expressions.ExpressionVisitor.Visit(ReadOnlyCollection`1 nodes)
   at System.Linq.Expressions.Compiler.VariableBinder.VisitLambda[T](Expression`1 node)
   at System.Linq.Expressions.Expression`1.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.Compiler.VariableBinder.Visit(Expression node)
   at System.Dynamic.Utils.ExpressionVisitorUtils.VisitArguments(ExpressionVisitor visitor, IArgumentProvider nodes)
   at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.Compiler.VariableBinder.Visit(Expression node)
   at System.Dynamic.Utils.ExpressionVisitorUtils.VisitArguments(ExpressionVisitor visitor, IArgumentProvider nodes)
   at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.Compiler.VariableBinder.Visit(Expression node)
   at System.Dynamic.Utils.ExpressionVisitorUtils.VisitArguments(ExpressionVisitor visitor, IArgumentProvider nodes)
   at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.Compiler.VariableBinder.Visit(Expression node)
   at System.Linq.Expressions.ExpressionVisitor.Visit(ReadOnlyCollection`1 nodes)
   at System.Linq.Expressions.Compiler.VariableBinder.VisitLambda[T](Expression`1 node)
   at System.Linq.Expressions.Expression`1.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.Compiler.VariableBinder.Visit(Expression node)
   at System.Linq.Expressions.Compiler.LambdaCompiler.Compile(LambdaExpression lambda)
   at System.Linq.Expressions.Expression`1.Compile(Boolean preferInterpretation)
   at System.Linq.Expressions.Expression`1.Compile()
   at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.CreateExecutorLambda[TResults]()
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVisitor.CreateExecutorLambda[TResults]()
   at Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor.CreateQueryExecutor[TResult](QueryModel queryModel)
   at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](QueryModel queryModel)
   --- End of stack trace from previous location where exception was thrown ---
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](Expression query, IQueryModelGenerator queryModelGenerator, IDatabase database, IDiagnosticsLogger`1 logger, Type contextType)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass13_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
   at Remotion.Linq.QueryableBase`1.GetEnumerator()
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.System.Collections.Generic.IEnumerable<TEntity>.GetEnumerator()
   at System.Collections.Generic.List`1.AddEnumerable(IEnumerable`1 enumerable)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)

提前致谢。

4

1 回答 1

2

Lambda 表达式参数是由instance绑定的,而不是像您根据代码所期望的那样按名称绑定。

你需要的基本上是首先创建一个参数表达式实例,将它传递给表达式访问者并在内部使用它来绑定映射的成员表达式,最后在创建新的 lambda 表达式时使用它。

所以修改visitor类如下:

public class CustomExpressionVisitor : ExpressionVisitor
{
    private readonly Type entityType;
    private readonly Type baseEntityType;
    private readonly ParameterExpression param;

    public CustomExpressionVisitor(Type entityType, Type baseEntityType, ParameterExpression param)
    {
        this.entityType = entityType;
        this.baseEntityType = baseEntityType;
        this.param = param;
    }
    protected override Expression VisitMember(MemberExpression expression)
    {
        if (expression.Member.DeclaringType == this.baseEntityType)
        {
            return Expression.PropertyOrField(this.param, expression.Member.Name);
        }

        return base.VisitMember(expression);
    }
}

调用代码为:

var param = Expression.Parameter(entity, "e");
var expressionVisitor = new CustomExpressionVisitor(entity, baseType, param);
var customExpression = expressionVisitor.Visit(expression?.Body);
var lambdaExpression = Expression.Lambda(customExpression, param);
于 2019-09-15T13:02:35.437 回答