多表连接LINQ查询中构建含Where子句的Lambda表达式问题
问题
需要在多表连接的LINQ查询中,为db1表添加Where()子句,原查询代码如下:
var query = from db1 in db.db1 join db2 in db.database2 on db1.atr1 equals db2.atr1 join db3 in db.database3 on db1.atr1 equals db3.atr1 select new { db1, db2, db3};
在无连接的查询中,构建Lambda表达式的Where子句可以直接写:
query = query.Where(t => t.attr5.Contains("String"));
但多表连接后,正确的写法应该是:
query = query.Where(t => t.db1.attr5.Contains("String"));
当前用表达式树构建Where子句的代码会抛出类型不匹配错误:
ParameterExpression eParam = Expression.Parameter(tableType, "innerTable"); MethodInfo method = filterValue.GetType().GetMethod("Contains", new Type[] { typeof(string) }); // filterValue is just a string MethodCallExpression call = Expression.Call(Expression.Property(eParam, filterField), method, Expression.Constant(filterValue)); Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(call, eParam); currentQuery = currentQuery.Where(lambda);
问题出在tableType和select new { db1, db2, db3 }生成的匿名类型不匹配,需要调整表达式树的构建逻辑。
解决方案
核心是先通过匿名类型的参数表达式获取db1属性,再链式访问db1的目标字段,具体步骤如下:
- 修正参数表达式的类型
参数表达式的类型必须是匿名类型(即查询结果的元素类型typeof(T)),而非db1的类型:
ParameterExpression eParam = Expression.Parameter(typeof(T), "item");
- 链式获取目标属性
先获取匿名类型中的db1属性,再获取db1的目标字段(比如attr5):
// 获取匿名类型的db1属性 Expression db1Expr = Expression.Property(eParam, "db1"); // 获取db1的目标字段 Expression targetAttrExpr = Expression.Property(db1Expr, filterField); // filterField为目标字段名,比如"attr5"
- 构建Contains方法调用
直接获取string类型的Contains方法,避免通过filterValue的类型获取(防止类型不一致):
MethodInfo containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) }); MethodCallExpression containsCall = Expression.Call(targetAttrExpr, containsMethod, Expression.Constant(filterValue));
- 构建Lambda并应用到查询
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(containsCall, eParam); currentQuery = currentQuery.Where(lambda);
完整修正代码
// T为匿名类型,可通过currentQuery.ElementType获取,或由编译器自动推断 ParameterExpression eParam = Expression.Parameter(typeof(T), "item"); // 链式访问item.db1.filterField Expression db1Property = Expression.Property(eParam, "db1"); Expression targetProperty = Expression.Property(db1Property, filterField); // 获取String.Contains方法 MethodInfo containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) }); MethodCallExpression containsCall = Expression.Call(targetProperty, containsMethod, Expression.Constant(filterValue)); // 构建Lambda表达式 Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(containsCall, eParam); // 应用Where子句 currentQuery = currentQuery.Where(lambda);
关键说明
原来的代码错误地将参数表达式的类型设为db1的类型,但查询的元素是匿名类型,所以必须以匿名类型作为参数,再通过属性访问拿到db1对象,之后才能访问db1的字段,这样就能解决类型不匹配的问题。
内容的提问来源于stack exchange,提问作者JoeD
相关产品推荐
相关产品推荐

