使用LinqKit PredicateBuilder进行相关模型(EF Core)

Igo*_*gor 7 c# linq linqkit entity-framework-core .net-core

我想使用LinqKit的PredicateBuilder并将谓词传递.Any给相关模型的方法.

所以我想构建一个谓词:

var castCondition = PredicateBuilder.New<CastInfo>(true);

if (movies != null && movies.Length > 0)
{
    castCondition = castCondition.And(c => movies.Contains(c.MovieId));
}
if (roleType > 0)
{
    castCondition = castCondition.And(c => c.RoleId == roleType);
}
Run Code Online (Sandbox Code Playgroud)

然后使用它来过滤与谓词中的模型有关的模型:

IQueryable<Name> result = _context.Name.AsExpandable().Where(n => n.CastInfo.Any(castCondition));
return await result.OrderBy(n => n.Name1).Take(25).ToListAsync();
Run Code Online (Sandbox Code Playgroud)

但这导致了 System.NotSupportedException: Could not parse expression 'n.CastInfo.Any(Convert(__castCondition_0, Func``2))': The given arguments did not match the expected arguments: Object of type 'System.Linq.Expressions.UnaryExpression' cannot be converted to type 'System.Linq.Expressions.LambdaExpression'.

我看到类似的问题和答案建议使用.Compile.或者另外一个构建额外谓词的问题.

所以我试着使用额外的谓词

var tp = PredicateBuilder.New<Name>(true);
tp = tp.And(n => n.CastInfo.Any(castCondition.Compile()));
IQueryable<Name> result = _context.Name.AsExpandable().Where(tp);
Run Code Online (Sandbox Code Playgroud)

或直接使用编译

IQueryable<Name> result = _context.Name.AsExpandable().Where(n => n.CastInfo.Any(castCondition.Compile()));
Run Code Online (Sandbox Code Playgroud)

但我有一个关于编译的错误: System.NotSupportedException: Could not parse expression 'n.CastInfo.Any(__Compile_0)'

那么是否有可能将PredicateBuilder的结果转换成Any

注意:我能够构建组合表达式的所需行为,但我不喜欢我需要额外的变量.

System.Linq.Expressions.Expression<Func<CastInfo,bool>> castExpression = (c => true);
if (movies != null && movies.Length > 0)
{
    castExpression = (c => movies.Contains(c.MovieId));
}
if (roleType > 0)
{
    var existingExpression = castExpression;
    castExpression = c => existingExpression.Invoke(c) && c.RoleId == roleType;
}
IQueryable<Name> result = _context.Name.AsExpandable().Where(n => n.CastInfo.Any(castExpression.Compile()));
return await result.OrderBy(n => n.Name1).Take(25).ToListAsync();
Run Code Online (Sandbox Code Playgroud)

所以我假设我只是想念建筑师.

关于版本的更新:我使用dotnet core 2.0和LinqKit.Microsoft.EntityFrameworkCore 1.1.10

Iva*_*oev 7

查看代码,可以假设castCondition变量的类型Expression<Func<CastInfo, bool>>(就像在早期版本中一样PredicateBuilder).

但如果是这种情况,那么n.CastInfo.Any(castCondition)甚至不应该编译(假设CastInfo是一个集合导航属性,所以编译器会命中Enumerable.Any哪个期望Func<CastInfo, bool>,而不是Expression<Func<CastInfo, bool>>).那么这里发生了什么?

在我看来,这是C#隐式操作符滥用的一个很好的例子.该PredicateBuilder.New<T>方法实际上返回一个名为的类ExpressionStarter<T>,它有许多方法模拟Expression,但更重要的是,它有隐式转换为Expression<Func<T, bool>>Func<CastInfo, bool>.后者允许该类用于顶级Enumerable/ Queryable方法作为相应的lambda func /表达式的替换.但是,它也可以防止在表达式树中使用时出现编译时错误,就像你的情况一样 - 编译器发出类似的东西n.CastInfo.Any((Func<CastInfo, bool>)castCondition),当然在运行时会导致异常.

LinqKit AsExpandable方法的整个想法是允许通过自定义Invoke扩展方法"调用"表达式,然后在表达式树中"扩展".所以回到开头,如果变量类型是Expression<Func<CastInfo, bool>>,则预期用途是:

_context.Name.AsExpandable().Where(n => n.CastInfo.Any(c => castCondition.Invoke(c)));
Run Code Online (Sandbox Code Playgroud)

但是由于之前解释的原因,现在这不能编译.所以你必须先将它转换为查询Expression<Func<T, bool> 之外:

Expression<Func<CastInfo, bool>> castPredicate = castCondition;
Run Code Online (Sandbox Code Playgroud)

然后使用

_context.Name.AsExpandable().Where(n => n.CastInfo.Any(c => castPredicate.Invoke(c)));
Run Code Online (Sandbox Code Playgroud)

要么

_context.Name.AsExpandable().Where(n => n.CastInfo.Any(castPredicate.Compile()));
Run Code Online (Sandbox Code Playgroud)

为了让编译器推断表达式类型,我将创建一个这样的自定义扩展方法:

using System;
using System.Linq.Expressions;

namespace LinqKit
{
    public static class Extensions
    {
        public static Expression<Func<T, bool>> ToExpression<T>(this ExpressionStarter<T> expr) => expr;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后简单地使用

var castPredicate = castCondition.ToExpression();
Run Code Online (Sandbox Code Playgroud)

它仍然有许多工作要做以外的查询,即下面就无法正常工作:

_context.Name.AsExpandable().Where(n => n.CastInfo.Any(c => castCondition.ToExpression().Invoke(c)));
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你的详细解释.我尝试了你的代码,不幸的是我有一个警告`LINQ表达式',其中__castCondition_0.Invoke([c])'无法翻译并将在本地进行评估.因此,当它编译并运行时,条件不会添加到SQL和查询选择所有行.你有什么建议吗? (2认同)
  • 啊,这是让我在这里的Q&A类型!(我有时觉得要放弃). (2认同)