可枚举相交和任何表达式不会编译为 SQL
Enumerable Intersect and Any Expressions do not compile to SQL
我正在尝试为 IQueryable 创建一个扩展方法,它获取一个函数 returning a IEnumerable<Int32>>
作为输入参数,应该对照另一个常量数字列表检查它。如果两个列表中至少包含一个相同的条目,则它应该 return 为真,类似于 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:值不能为空。 bei System.Linq.Expressions.Expression.Lambda(表达式主体、字符串名称、布尔尾调用、IEnumerable`1 参数)
Intersect 查询可能有什么问题?
长话短说:我没有找到让 Intersect()
正常工作的任何解决方案。相反,我更改了我的 Linq 表达式以摆脱相交。我会 post 我的用例的解决方案,也许对以后的人有用。
想法是使用
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);
}
我正在尝试为 IQueryable 创建一个扩展方法,它获取一个函数 returning a IEnumerable<Int32>>
作为输入参数,应该对照另一个常量数字列表检查它。如果两个列表中至少包含一个相同的条目,则它应该 return 为真,类似于 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:值不能为空。 bei System.Linq.Expressions.Expression.Lambda(表达式主体、字符串名称、布尔尾调用、IEnumerable`1 参数)
Intersect 查询可能有什么问题?
长话短说:我没有找到让 Intersect()
正常工作的任何解决方案。相反,我更改了我的 Linq 表达式以摆脱相交。我会 post 我的用例的解决方案,也许对以后的人有用。
想法是使用
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);
}