EF Core 全局查询过滤器复杂表达式

Posted

技术标签:

【中文标题】EF Core 全局查询过滤器复杂表达式【英文标题】:EF Core Global Query Filter Complex expression 【发布时间】:2021-12-25 03:57:19 【问题描述】:

我正在尝试在我的应用程序中实现一个用于租赁的全局查询过滤器。我有AssessmentModels,它可以有多个所有者,这些所有者来自我无法完全控制的第三方,但可以根据需要进行调整。 (并且可以在我保存到我的数据库之前进行操作)现在,Owners 存储为分号分隔的字符串(例如,team1;team2)。

我提出了以下适用于选择数据但不适用于全局查询过滤器的方法:

private Expression<Func<AssessmentModel, bool>> GetAssessmentFilter()

    // The lambda parameter.
    var assessmentParameter = Expression.Parameter(typeof(AssessmentModel), "a");
    // Build the individual conditions to check against.
    var orConditions = _adminTeamNames
        .Select(keyword => (Expression<Func<AssessmentModel, bool>>)(a => EF.Functions.Like(a.Owners, $"%keyword%")))
        .Select(lambda => (Expression)Expression.Invoke(lambda, assessmentParameter))
        .ToList();

    // Combine the individual conditions to an expression tree of nested ORs.
    var orExpressionTree = orConditions
        .Skip(1)
        .Aggregate(
            orConditions.First(),
            (current, expression) => Expression.OrElse(expression, current));


    // Build the final predicate (a lambda expression), so we can use it inside of `.Where()`.
    var predicateExpression = (Expression<Func<AssessmentModel, bool>>)Expression.Lambda(
        orExpressionTree,
        assessmentParameter);

    return predicateExpression;

所以, var result = db.Assessments.Where(predicateExpression).ToList(); 有效,但 modelBuilder.Entity&lt;AssessmentModel&gt;().HasQueryFilter(predicateExpression); 给出错误:

System.InvalidOperationException : The LINQ expression 'DbSet<AssessmentModel>()
.Where(a => Invoke(a => value(DbFunctions).Like(a.Owners, "%Los%"), a)
 || Invoke(a => value(DbFunctions).Like(a.Owners, "%Atl%"), a)
)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

【问题讨论】:

【参考方案1】:

AFAIK EF Core 不擅长翻译调用表达式。尝试用您的自定义参数表达式(即assessmentParameter)手动替换构建 lambda 的参数:

var orConditions = _adminTeamNames
    .Select(keyword => (Expression<Func<AssessmentModel, bool>>)(a => EF.Functions.Like(a.Owners, $"%keyword%")))
    .Select(lambda => new ReplacingExpressionVisitor(lambda.Parameters, new []assessmentParameter).Visit(lambda.Body))
    .ToList();

【讨论】:

以上是关于EF Core 全局查询过滤器复杂表达式的主要内容,如果未能解决你的问题,请参考以下文章

ef 通过反射和表达式树配置全局过滤筛选器

EF Core 中实现 动态数据过滤器

利用EF Core的Lambda表达式和Join进行多表查询

利用EF Core的Lambda表达式和Join进行多表查询

无法翻译 LINQ 表达式。以可翻译的形式重写查询,或切换到客户端评估 EF Core 3.1

比较 EF Core Linq 查询中的 DateTime