如何实现EF中针对不同关联子实体code属性的通用Where查询方法
EF通用关联子实体Code查询实现错误分析与解决方案
核心错误点
- 类型不匹配:方法定义的第一个参数是
Func<T, string>,要求传入以子实体为入参的委托,但你调用时传入的d=>d.ChildEntity.Code入参d是主实体MainTable类型,编译都无法通过 - 委托无法转SQL:你用了
Func系列内存委托,EF查询提供器只能解析Expression表达式树生成SQL,委托会触发客户端计算甚至直接报错 - 语法错误:Where子句里的
d变量未定义,你声明的lambda参数是q却完全没用到,原代码还多写了一个尖括号List< string>> - Include逻辑不可靠:硬编码用实体类名作为导航属性名,一旦导航属性名和类名不一致,关联数据加载直接失败
修正后的实现
首先引入表达式树相关命名空间:
using System.Linq.Expressions;
场景1:一对一/多对一关联(主表导航属性为单个子实体)
private IQueryable<MainTable> OneLevelSearch<T>( Expression<Func<MainTable, T>> navigationSelector, Func<T, string> codeSelector, List<string> codeList, IQueryable<MainTable> efQueryable) { if (codeList == null || !codeList.Any()) return efQueryable; var distinctCodes = codeList.Distinct().ToList(); var parameter = Expression.Parameter(typeof(MainTable), "m"); var navigationInvoke = Expression.Invoke(navigationSelector, parameter); var codeInvoke = Expression.Invoke(Expression.Lambda<Func<T, string>>(codeSelector), navigationInvoke); var containsMethod = typeof(List<string>).GetMethod("Contains", new[] { typeof(string) }); var containsExpression = Expression.Call(Expression.Constant(distinctCodes), containsMethod, codeInvoke); var filter = Expression.Lambda<Func<MainTable, bool>>(containsExpression, parameter); return efQueryable.Include(navigationSelector.GetMemberAccessPath()).Where(filter); } // 辅助方法:获取表达式对应的导航路径用于Include public static string GetMemberAccessPath(this Expression expression) { var path = new List<string>(); var memberExpression = expression as MemberExpression; while (memberExpression != null) { path.Insert(0, memberExpression.Member.Name); memberExpression = memberExpression.Expression as MemberExpression; } return string.Join(".", path); }
调用方式:
efQueryable = OneLevelSearch<ChildEntity>(m => m.ChildEntity, c => c.Code, CodeList, efQueryable);
场景2:一对多/多对多关联(主表导航属性为子实体集合)
private IQueryable<MainTable> OneLevelCollectionSearch<T>( Expression<Func<MainTable, IEnumerable<T>>> collectionNavigationSelector, Func<T, string> codeSelector, List<string> codeList, IQueryable<MainTable> efQueryable) { if (codeList == null || !codeList.Any()) return efQueryable; var distinctCodes = codeList.Distinct().ToList(); var parameter = Expression.Parameter(typeof(MainTable), "m"); var collectionInvoke = Expression.Invoke(collectionNavigationSelector, parameter); // 构造子实体匹配条件 var childParam = Expression.Parameter(typeof(T), "c"); var codeInvoke = Expression.Invoke(Expression.Lambda<Func<T, string>>(codeSelector), childParam); var containsMethod = typeof(List<string>).GetMethod("Contains", new[] { typeof(string) }); var containsExpr = Expression.Call(Expression.Constant(distinctCodes), containsMethod, codeInvoke); var anyPredicate = Expression.Lambda<Func<T, bool>>(containsExpr, childParam); // 构造集合Any判断逻辑 var anyMethod = typeof(Enumerable).GetMethods() .First(m => m.Name == "Any" && m.GetParameters().Length == 2) .MakeGenericMethod(typeof(T)); var anyExpr = Expression.Call(anyMethod, collectionInvoke, anyPredicate); var filter = Expression.Lambda<Func<MainTable, bool>>(anyExpr, parameter); return efQueryable.Include(collectionNavigationSelector.GetMemberAccessPath()).Where(filter); }
调用方式:
efQueryable = OneLevelCollectionSearch<ChildEntity>(m => m.ChildEntityList, c => c.Code, CodeList, efQueryable);
内容的提问来源于stack exchange,提问作者Jerker Pihl
相关产品推荐
相关产品推荐

