EF Core 5使用具体类型构建查询条件 解决LINQ表达式无法转换问题
问题根本原因
EF Core 3.0 之后移除了默认自动客户端评估的逻辑,所有无法转换为SQL的LINQ逻辑都会直接抛出异常,你在EF Core 2上能运行确实是因为当时不可翻译的部分被自动放到内存中执行,属于隐式客户端评估,性能差且容易产生非预期结果。
当前报错的核心是:EF Core的SQL翻译器无法识别Tuple的构造函数调用,也不支持对构造出来的Tuple实例的Item1/Item2属性做SQL层面的解析,换成自定义类如果用构造函数传参赋值也会触发同样的问题。
可行解决方案
方案1(推荐):自定义类型使用属性初始化而非构造函数传参
EF Core原生支持对属性初始化写法的强类型投影做翻译,你可以先定义一个强类型的结果包装类,然后在Join的resultSelector中用属性初始化的方式赋值,就可以正常配合PredicateBuilder使用:
首先定义包装类:
public class TestJoinResult { public Test1 Item1 { get; set; } public Test2 Item2 { get; set; } }
调整查询写法:
var result = context.Test1 .Join(context.Test2, q => q.Test11, q => q.Test21, (q, p) => new TestJoinResult { Item1 = q, Item2 = p }) .Where(GetTransactionsSearchPredicate(values)) .ToList();
对应的PredicateBuilder方法直接把泛型参数换成TestJoinResult即可:
private static Expression<Func<TestJoinResult, bool>> GetTransactionsSearchPredicate(string[] values) { var searchPredicate = PredicateBuilder.False<TestJoinResult>(); foreach (var val in values) { searchPredicate = searchPredicate.Or(q => q.Item1.Test2 == val); } return searchPredicate; }
方案2:先过滤再Join(适用过滤条件仅涉及单表的场景)
如果你的动态过滤条件都只涉及Test1表的字段,可以先对Test1做过滤,再执行Join操作,完全避免投影包装类型的问题:
// 直接构造针对Test1的过滤表达式 private static Expression<Func<Test1, bool>> GetTest1Filter(string[] values) { var searchPredicate = PredicateBuilder.False<Test1>(); foreach (var val in values) { searchPredicate = searchPredicate.Or(q => q.Test2 == val); } return searchPredicate; } // 执行查询 var result = context.Test1 .Where(GetTest1Filter(values)) .Join(context.Test2, q => q.Test11, q => q.Test21, (q,p) => new {q,p}) .ToList();
内容的提问来源于stack exchange,提问作者bpiec
相关产品推荐
相关产品推荐

