如何为LINQ连接生成的匿名类型动态构造表达式树?
解决匿名类型动态Lambda表达式的问题
我之前也碰到过一模一样的困扰——用LINQ连接两个实体类后,没法给返回的匿名类型动态生成Lambda表达式做过滤,因为匿名类型的字段没有预定义,动态表达式根本找不到对应的属性,传object或者dynamic都会直接抛出异常。
先说说我的初始场景
先给大家看下我用到的实体类:
public class Book { public int BookID { get; set; } public string Author { get; set; } public string Publisher { get; set; } } public class Library { public int ID { get; set; } public Book Book { get; set; } public int Count { get; set; } }
还有那个动态生成Lambda的函数,单独查询Book或者Library的时候完全正常:
public static Expression<Func<T, bool>> GetLambdaExpression<T>(List<Operation> OperationList) { ExpressionTree expressionTree = new ExpressionTree(); Node Root = expressionTree.ConstructTree(OperationList); var Parameter = Expression.Parameter(typeof(T), "x"); var Expression = CalculateExpression(Root); // 通过And/Or组合OperationList中的输入生成表达式子句 return Expression.Lambda<Func<T, bool>>(Expression, Parameter); // 最终创建Lambda }
但一旦做实体连接,返回匿名类型后就彻底失效了:
// 这段代码无法正常运行 var result = from c in _BookContext.Books join d in _LibraryContext.Library on c.BookID equals d.ID select new { c , d }; var OperationList = // 客户端传递的过滤条件 var LambdaExpression = GetLambdaExpression<object>(OperationList); // 传object/dynamic都会报错 result.Where(LambdaExpression);
我的解决方案
后来我换了个思路,放弃匿名类型,专门创建一个包含连接实体的类:
public class JoinResult { public Book Book { get; set; } public Library Library { get; set; } }
然后把连接结果映射到这个类里:
var result = from c in _BookContext.Books join d in _LibraryContext.Library on c.BookID equals d.ID select new JoinResult{ Book = c , Library = d };
接下来是关键步骤:修改动态表达式的生成逻辑,处理嵌套属性(比如x.Book.BookID这种多层属性)。举个Equal操作的实现例子:
public static IQueryable<T> PerformEqualOperation<T>(int Constant, string FieldName, Type Prop, IQueryable<T> resultOfJoin) { var Parameter = Expression.Parameter(typeof(T), "x"); // 对应表达式中的x // 获取JoinResult的所有属性 PropertyInfo[] Properties = typeof(T).GetProperties(); string propertyname = string.Empty; // 找到匹配目标类型的属性名(比如Prop是Book类型,就找到JoinResult里的Book属性) foreach(var property in Properties) { if(property.PropertyType == Prop) { propertyname = property.Name; break; } } // 构建第一层属性表达式:x.Book var expressionparameter = Expression.Property(Parameter, propertyname); // 构建深层属性表达式:x.Book.BookID var expressionproperty = Expression.Property(expressionparameter, FieldName); // 构建等于条件表达式:x.Book.BookID == Constant var expressionclause = Expression.Equal(expressionproperty, Expression.Constant(Constant)); // 生成最终的Lambda表达式 var expressionlambda = Expression.Lambda<Func<T,bool>>(expressionclause, Parameter); return resultOfJoin.Where(expressionlambda).AsQueryable(); }
这样就能正常对连接后的结果进行动态条件过滤了!
内容的提问来源于stack exchange,提问作者wsabimayhm
相关产品推荐
相关产品推荐

