gpt4 book ai didi

c# - EF Core 5 - 内存对象列表过滤不适用于 DBset

转载 作者:行者123 更新时间:2023-12-04 07:38:50 26 4
gpt4 key购买 nike

我需要使用作为变量传递给我的存储库的对象列表来过滤 DBset。
以下在 EF6 中工作

List<MyFilterObj> incomingList = [{A: 1, B: 2}, {A: 3, B: 4}]

return await context.MyDBset
.Where(x => incomingList.Any(v =>
v.A == x.A &&
v.B == x.B)).ToListAsync();
如果我在 EF Core 5 中尝试同样的方法,我会收到以下错误:
无法翻译 LINQ 表达式 (MyDBset)。以可翻译的形式重写查询,或通过插入对“AsEnumerable”、“AsAsyncEnumerable”、“ToList”或“ToListAsync”的调用显式切换到客户端评估。见 https://go.microsoft.com/fwlink/?linkid=2101038了解更多信息。
我不能将传入列表分成单独的 A 和 B 列表,因为组合对于过滤器的准确性至关重要。否则我本可以使用这种方法
return await context.MyDBset
.Where(x => incomingListA.Contains(x.A) &&
incomingListB.Contains(x.B)).ToListAsync();
这种方法确实适用于 EF Core 5
基本上我希望 EF 将我的查询翻译成如下内容:
SELECT * FROM MyTable AS m
Where (m.A = 1 AND m.B = 2) OR (m.A = 3 AND m.B = 4)
如果我遍历我的incomingList,将“where”过滤器添加到查询中,则生成的SQL使用“AND”而不是“OR”,如下所示:
SELECT * FROM MyTable AS m
Where (m.A = 1 AND m.B = 2) AND (m.A = 3 AND m.B = 4)
现在我的工作涉及使用一些基本过滤器将结果集绑定(bind)到内存中,然后使用原始语法运行内存过滤器。这显然不理想。
有什么建议么?谢谢

最佳答案

由于 EF Core 不愿意翻译这样的表达式(众所周知的限制是 Contains 具有原始值是 L2E 查询中唯一支持的内存收集操作,因为它直接转换为 SQL IN 运算符),你有自己做。 Any基本上相当于Or正如你所提到的,所以对于不是那么大的列表和顶级查询,可以通过使用一些谓词构建器实现来动态构建 || 来轻松完成。谓词。或者直接使用Expression我对 How to simplify repetitive OR condition in Where(e => e.prop1.contains() || e.prop2.contains() || ...) 的回答中的自定义扩展方法中的类方法:


public static partial class QueryableExtensions
{
public static IQueryable<T> WhereAnyMatch<T, V>(this IQueryable<T> source, IEnumerable<V> values, Expression<Func<T, V, bool>> match)
{
var parameter = match.Parameters[0];
var body = values
// the easiest way to let EF Core use parameter in the SQL query rather than literal value
.Select(value => ((Expression<Func<V>>)(() => value)).Body)
.Select(value => Expression.Invoke(match, parameter, value))
.Aggregate<Expression>(Expression.OrElse);
var predicate = Expression.Lambda<Func<T, bool>>(body, parameter);
return source.Where(predicate);
}
}

为您的场景提供示例用法:
context.MyDBset
.WhereAnyMatch(incomingList, (x, v) => v.A == x.A && v.B == x.B)
通常而不是 Expression.Invoke像这样的辅助实用程序将使用自定义 ExpressionVisitor替换源表达式的参数(因此模拟“调用”),这会生成看起来更自然的查询表达式,但对于 EF Core 来说并不重要,因为它可以识别并正确翻译表达式树中的调用表达式。
但以防万一 Expression.Invoke不起作用,这是参数替换版本 - 它应该适用于所有查询提供程序。一、 helper

public static partial class ExpressionBuilder
{
public static Expression ReplaceParameter(this Expression source, ParameterExpression parameter, Expression value)
=> new ParameterReplacer { Parameter = parameter, Value = value }.Visit(source);

class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Parameter;
public Expression Value;
protected override Expression VisitParameter(ParameterExpression node)
=> node == Parameter ? Value : node;
}
}
然后简单地替换调用
.Select(value => Expression.Invoke(match, parameter, value))
.Select(value => match.Body.ReplaceParameter(match.Parameters[1], value))
因为特别是这里我们重用了第一个参数( var parameter = match.Parameters[0]; ),否则它也应该被替换。

关于c# - EF Core 5 - 内存对象列表过滤不适用于 DBset,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67597708/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com