如何将 Func<T, bool> 转换为 Expression<Func<T, bool>>

mz1*_*378 2 c# lambda expression expression-trees entity-framework-core

我使用这段代码,但出现错误:

错误:System.ArgumentException:“...GreenPaperItem”类型的表达式不能用于方法“Boolean”的“System.Runtime.CompilerServices.Closure”类型的参数

public static Expression<Func<T, bool>> ToExpression<T>(Func<T, bool> p)
{
     ParameterExpression p0 = Expression.Parameter(typeof(T));
     ParameterExpression p1 = Expression.Parameter(typeof(bool));
     return Expression.Lambda<Func<T, bool>>(Expression.Call(p.Method, p0, p1),
           new ParameterExpression[] { p0, p1 });
}
Run Code Online (Sandbox Code Playgroud)

该表达式旨在用于 linq toEntity IQueriyable 查询:

 query = query.Where(ToExpression<GreenPaperItem>(filter.GenerateFilterFunction()));
Run Code Online (Sandbox Code Playgroud)

Gur*_*ron 5

如何转换Func<T, bool>Expression<Func<T, bool>>

简而言之 - 你不知道。反之亦然,通过框架Expression<TDelegate>.Compile方法提供的转换是“容易”的。但其他方法将需要执行诸如从 IL/机器代码反编译之类的操作(我想我看到了一些尝试,但不确定何时何地 =) )。

我使用过AsQueryable()并且它有效,但是为了检查它最后是否不可枚举,我收到错误“给定的‘IQueryable’不支持生成查询字符串。” 当我想转换查询时ToQueryString()

所以基本上它不起作用(至少以你真正想要/需要的方式)。

您似乎对IQueryable表达式树和查询提供程序(尤其是 EF Core 之一)的工作方式存在一些误解。简而言之(经过一些简化)通常查询提供程序将分析传递的表达式树以执行某些操作。在 EF Core 情况下,它会将调用转换为实际的 SQL 查询,并且无法将任意方法调用转换为 SQL,这无法通过toFunc<...>返回的“转换”来修复。filter.GenerateFilterFunction()Expression<Func<...>>

您的无效尝试:

public static Expression<Func<T, bool>> ToExpression<T>(Func<T, bool> p)
{
     ParameterExpression p0 = Expression.Parameter(typeof(T));
     ParameterExpression p1 = Expression.Parameter(typeof(bool));
     return Expression.Lambda<Func<T, bool>>(Expression.Call(p.Method, p0, p1),
           new ParameterExpression[] { p0, p1 });
}
Run Code Online (Sandbox Code Playgroud)

与以下内容没有太大区别:

query.Where(gpi => filter.GenerateFilterFunction()(gpi)) // allow the compiler to do the magic for you
Run Code Online (Sandbox Code Playgroud)

这将有同样的问题 -GenerateFilterFunction返回函数查询提供程序(EF Core)一无所知,并且无法将其转换为 SQL(出于明显的原因)。

以及您从评论中的尝试:

query = query
   .Where(filter.GenerateFilterFunction())
   .AsQueryable();
Run Code Online (Sandbox Code Playgroud)

将导致所有内容都.Where(filter.GenerateFilterFunction())将在客户端执行,将所有内容提取到内存中,而不在数据库端进行过滤(Enumerable.Where(Func<>)将使用而不是,与之前的Queryable.Where(Expression<Func<>>)显式调用效果基本相同)。以下内容没有任何重大影响,因为它将可通过内存中的对象进行查询。观察到的行为也证明了这一点。AsEnumerable()WhereAsQueryableEntityFrameworkQueryableExtensions.ToQueryString(IQueryable)

您有多种选择:

  1. 引入GenerateFilterExpression实际构建可翻译的方法Expression<Func<T, bool>>

  2. 相当常见的方法是仅利用“动态”组合的能力Where(当您需要 AND 组合逻辑时):

    if(filter.IsProp1Provided)
    {
        query = query.Where(e => e.Col1 == filter.Prop1);
    }
    
    if(filter.IsAnotherPropProvided)
    {
        query = query.Where(e => e.AnotherCol == filter.AnotherProp);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. PredicateBuilder从 LINQKit使用(可能除了第 2 点之外)

也可以看看: