【问题标题】:Enumerable Intersect and Any Expressions do not compile to SQLEnumerable Intersect 和 Any Expressions 不编译为 SQL
【发布时间】:2021-02-17 09:39:07
【问题描述】:

我正在尝试为 IQueryable 创建一个扩展方法,该方法获取一个返回 IEnumerable<Int32>> 作为输入参数的函数,应对照另一个常数列表检查该函数。如果两个列表中至少包含一个相同的条目,则它应该返回 true,例如 listA.Intersect(listB).Any()。此表达式还必须很好地编译为 SQL(最新的 EF 核心)(Intersect()Any() 自己应该这样做)。所以这是我想出的(还):

public static IQueryable<T> AuthorizedRecords<T>(this IQueryable<T> query, Expression<Func<T, IEnumerable<Int32>>> employeeIds)
{
    var ids = (new List<int> { 1, 2, 3 }).AsEnumerable(); // <- replaced later
    
    var methodAny = typeof(Enumerable)
        .GetMethods()
        .Where(m => m.Name == "Any" && m.GetParameters().Length == 1)
        .First()
        .MakeGenericMethod(typeof(int));

    var methodIntersect = typeof(Enumerable)
         .GetMethods()
         .Where(m => m.Name == "Intersect" && m.GetParameters().Length == 2)
         .First()
         .MakeGenericMethod(typeof(int));

    var lambdaParam = employeeIds.Parameters.Single();
    var lambda = Expression.Lambda(
        Expression.Call(
            methodAny,
            Expression.Call(
                methodIntersect,
                Expression.Constant(ids), 
                employeeIds.Body
            )
        ),
        lambdaParam
     );
     
     var predicate = (Expression<Func<T, bool>>)lambda;
     return query.Where(predicate);
}       

此代码可以编译,但我得到一个运行时错误,即 LINQ 表达式 <...> 无法转换为 sql。表达有什么问题?


更新

表达式似乎没问题,但 Intersect() 方法由于某种原因失败了。我将代码简化为重现错误的最小 Linq 查询:

dbContext.Meetings
  .Where(e => e.AccessList
    .Select(x => x.Id)
    .Intersect((new List<int>() { 90, 91, 92 })
    .Any()
  );

现在我得到一个 System.ArgumentNullException: Value cannot be null。 bei System.Linq.Expressions.Expression.Lambda(表达式主体,字符串名称,布尔tailCall,IEnumerable`1个参数)

Intersect 查询可能有什么问题?

【问题讨论】:

  • 您正在使用EnumerableAnyIntersect 方法,而不是QueryableEnumerable 是内存中的 LINQ。
  • 将其更改为 Queryable 并从文档中注意到 Intersect&lt;TSource&gt;(IQueryable&lt;TSource&gt;, IEnumerable&lt;TSource&gt;) 的第二个参数必须是 IEnumerable&lt;TSource&gt;,因此我将它们交换为 Expression.call(...)。当使用 dbContext.Meetings.AuthorizedRecords(e =&gt; e.AccessList.Select(x =&gt; x.Id).AsQueryable() 调用它时,我得到一个运行时错误 Value cannot be null。 (参数“参数”)。当使用 const List 而不是 e =&gt; e.AccessList.Select(x 时,一切正常。有什么建议吗?
  • 你能用最新的代码/错误更新你的问题吗?
  • 请看下面我的回答,我已经在那里发布了我的工作代码示例

标签: c# lambda linq-to-sql expression


【解决方案1】:

长话短说:我没有找到任何解决方案让Intersect() 工作。相反,我改变了我的 Linq 表达式以摆脱 Intersect。我将发布我的用例的解决方案,也许以后对某人有用。

想法是使用

Where(ListA.Any(x => ListB.Contains(x))
// instead of 
// Where(ListA.Intersect(ListB).Any()

这里是:

public static IQueryable<T> AuthorizedRecords<T>(this IQueryable<T> query, Expression<Func<T, IQueryable<int>>> employeeIds)
{
  var ids = new List<int> { 1, 2, 3 }); // <- changed later

  var methodAny = typeof(Queryable)
        .GetMethods()
        .First(m => m.Name == "Any" && m.GetParameters().Length == 2)
        .MakeGenericMethod(typeof(int));

  Expression<Func<int, bool>> contains = x => ids.Contains(x);
  
  var lambda = Expression.Lambda<Func<T, bool>>(
    Expression.Call(
      methodAny,
      employeeIds.Body,
      contains
    ), 
    employeeIds.Parameters
  );
  
  return query.Where(lambda);
}     

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-08-06
    • 2015-11-23
    • 2011-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-17
    • 2017-10-22
    相关资源
    最近更新 更多