表达式树 学习笔记
参考 :
http://www.cnblogs.com/jesse2013/p/expressiontree-part1.html
https://blog.csdn.net/WuLex/article/details/80766862 表达式树, 委托,匿名函数,lambda 表达式, Action, Func 的关系
讲讲 上面这些的关系
委托是古老的, 后来有了泛型就用 Action 和 Func
然后开始玩匿名就有了 lambda 表达式.
lambda 基本上可以等价于委托和匿名函数.
表达式树的用途是表达逻辑,然后存起来传来传去,解析,修改等等.
它的其中一个用途是可以用来做 lambda 表达式,然后就可以创建出委托, Action Func
当然它还有更广的用途啦.
所以如果我们想同台创建一个匿名函数,就可以创建表达式树,然后 Expression.Lambda( delegateType, expression ... ).Complie() 这样来使用啦
里面有教
Expression.Loop
Expression.Call
Expression.Constant
Expression.Block
Expression.Lambda
Expression.Parameter
Expression.Assign =
Expression.AddAssign
Expression.DivideAssign
Expression.Label
Expression.IfThenElse
Expression.LessThanOrEqual
Expression.PostIncrementAssign ++
Expression.Break
Expression.TypeIs ("spruce" Is Int32)
Expression.ArrayAccess array[0]
Expression.New
如果你认真想学习的话,请去上面这个链接。
public object getProps<T>(DbSet<T> query, int id, string navigationPropName) where T : class { //我们想 return db.?.Where(c => c.id == ?).SelectMany(c => c.?); //或者想 return db.?.Include(c => c.?).FirstOrDefaultAsync(c => c.id == ?).Result.? //? 表示了动态的值 //note : include 使用 Expression.Call 会error, 用 Invoke 就不会, 不清楚为什么 //note : 反射选择方法时,是直接选取第几个,一但 dll 版本换了, 可能就会被影响了 (以后如果想彻底解决的话 : http://stackoverflow.com/questions/3631547/select-right-generic-method-with-reflection) Type entityType = typeof(T); //e.g. Color Type navigationPropType = entityType.GetProperty(navigationPropName).PropertyType; //e.g. List<Size> if (navigationPropType.IsGenericType) //is List 的情况 { //添加 where //e.g. db.colors.where(c => c.id == id); //e.g. Expression<Func<Color, bool>> exp = c => c.id == 1; //e.g. var query = Queryable.Where<Color>(db.colors, exp); Type delegateType = typeof(Func<,>).MakeGenericType(entityType, typeof(bool)); //e.g. c => c.id == 1 ParameterExpression parameter = Expression.Parameter(entityType, "c"); ConstantExpression idValue = Expression.Constant(id, typeof(int)); Expression idProp = Expression.Property(parameter, "id"); BinaryExpression body = Expression.Equal(idProp, idValue); //需要组合2次哦 LambdaExpression lambda = Expression.Lambda(body, new[] { parameter }); lambda = Expression.Lambda(delegateType, lambda.Body, lambda.Parameters); IQueryable source = (IQueryable)(query); var queryAfterWhere = source.Provider.CreateQuery(Expression.Call( typeof(Queryable), "Where", //Queryable.Where new Type[] { entityType }, //e.g. <Color> source.Expression, Expression.Quote(lambda))); //e.g. (db.colors, exp) //添加 selectMany //e.g. db.colors.where(c => c.id == id).selectMany(c => c.sizes); //e.g. Expression<Func<Color, IEnumerable<Size>>> exp = c => c.sizes; //e.g. var query = Queryable.SelectMany<Color,Size>(db.colors, exp); Type navigationPropSingleType = navigationPropType.GetGenericArguments()[0]; //e.g. Size Type enumerableType = typeof(IEnumerable<>).MakeGenericType(navigationPropSingleType); //e.g. IEnumerable<Size> delegateType = typeof(Func<,>).MakeGenericType(entityType, enumerableType); //e.g. Func<Color, IEnumerable<Size>> //e.g. c => c.sizes parameter = Expression.Parameter(entityType, "c"); Expression navigationProp = Expression.Property(parameter, navigationPropName); lambda = Expression.Lambda(navigationProp, new[] { parameter }); //e.g. Expression<Func<Color, IEnumerable<Size>>> exp = c => c.sizes lambda = Expression.Lambda(delegateType, lambda.Body, lambda.Parameters); source = (IQueryable)(queryAfterWhere); var queryAfterSelectMany = source.Provider.CreateQuery(Expression.Call( typeof(Queryable), "SelectMany", //Queryable.SelectMany new Type[] { entityType, navigationPropSingleType }, //e.g. <Color, Size> source.Expression, Expression.Quote(lambda))); //e.g. (db.colors, exp) return queryAfterSelectMany; } else { //这里和上面不同之处就是使用了反射Invoke来调用方法,而上面是使用Expression.Call //这里用 Expression.Call 会error, 没搞懂为什么啦 Type delegateType = typeof(Func<,>).MakeGenericType(entityType, navigationPropType); var parameter = Expression.Parameter(entityType, "c"); Expression navigationProp = Expression.Property(parameter, navigationPropName); var lambda = Expression.Lambda(navigationProp, new[] { parameter }); lambda = Expression.Lambda(delegateType, lambda.Body, lambda.Parameters); var methods = typeof(QueryableExtensions).GetMethods().Where(m => m.Name == "Include").ToList(); var method = methods[2].MakeGenericMethod(typeof(Color), typeof(Product)); var queryAfterInclude = (IQueryable)method.Invoke(null, new object[] { query, lambda }); delegateType = typeof(Func<,>).MakeGenericType(entityType, typeof(bool)); ConstantExpression idValue = Expression.Constant(1, typeof(int)); Expression idProp = Expression.Property(parameter, "id"); BinaryExpression body = Expression.Equal(idProp, idValue); lambda = Expression.Lambda(body, new[] { parameter }); lambda = Expression.Lambda(delegateType, lambda.Body, lambda.Parameters); methods = typeof(QueryableExtensions).GetMethods().Where(m => m.Name == "FirstOrDefaultAsync").ToList(); method = methods[2].MakeGenericMethod(entityType); var result = method.Invoke(null, new object[] { queryAfterInclude, lambda }); var list = result.GetType().GetProperty("Result").GetValue(result, null); var navigationResource = list.GetType().GetProperty(navigationPropName).GetValue(list, null); return navigationResource; } }