Mercurial > pub > bltoolkit
view Source/Linq/ExpressionHelper.cs @ 4:f757da6161a1
!bug 100 + 2h fixed gregression
author | cin |
---|---|
date | Sun, 24 Aug 2014 17:57:42 +0400 |
parents | f990fcb411a9 |
children |
line wrap: on
line source
using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using BLToolkit.Data.Linq.Builder; // ReSharper disable ConditionIsAlwaysTrueOrFalse // ReSharper disable HeuristicUnreachableCode namespace BLToolkit.Linq { using Data.Linq; using Reflection; public static class ExpressionHelper { #region IsConstant public static bool IsConstant(Type type) { if (type.IsEnum) return true; switch (Type.GetTypeCode(type)) { case TypeCode.Int16 : case TypeCode.Int32 : case TypeCode.Int64 : case TypeCode.UInt16 : case TypeCode.UInt32 : case TypeCode.UInt64 : case TypeCode.SByte : case TypeCode.Byte : case TypeCode.Decimal : case TypeCode.Double : case TypeCode.Single : case TypeCode.Boolean : case TypeCode.String : case TypeCode.Char : return true; } if (TypeHelper.IsNullableType(type)) return IsConstant(type.GetGenericArguments()[0]); return false; } #endregion #region Compare internal static bool Compare(Expression expr1, Expression expr2, Dictionary<Expression,QueryableAccessor> queryableAccessorDic) { return Compare(expr1, expr2, new HashSet<Expression>(), queryableAccessorDic); } static bool Compare( Expression expr1, Expression expr2, HashSet<Expression> visited, Dictionary<Expression,QueryableAccessor> queryableAccessorDic) { if (expr1 == expr2) return true; if (expr1 == null || expr2 == null || expr1.NodeType != expr2.NodeType || expr1.Type != expr2.Type) return false; switch (expr1.NodeType) { case ExpressionType.Add: case ExpressionType.AddChecked: case ExpressionType.And: case ExpressionType.AndAlso: case ExpressionType.ArrayIndex: #if FW4 || SILVERLIGHT case ExpressionType.Assign: #endif case ExpressionType.Coalesce: case ExpressionType.Divide: case ExpressionType.Equal: case ExpressionType.ExclusiveOr: case ExpressionType.GreaterThan: case ExpressionType.GreaterThanOrEqual: case ExpressionType.LeftShift: case ExpressionType.LessThan: case ExpressionType.LessThanOrEqual: case ExpressionType.Modulo: case ExpressionType.Multiply: case ExpressionType.MultiplyChecked: case ExpressionType.NotEqual: case ExpressionType.Or: case ExpressionType.OrElse: case ExpressionType.Power: case ExpressionType.RightShift: case ExpressionType.Subtract: case ExpressionType.SubtractChecked: { var e1 = (BinaryExpression)expr1; var e2 = (BinaryExpression)expr2; return e1.Method == e2.Method && Compare(e1.Conversion, e2.Conversion, visited, queryableAccessorDic) && Compare(e1.Left, e2.Left, visited, queryableAccessorDic) && Compare(e1.Right, e2.Right, visited, queryableAccessorDic); } case ExpressionType.ArrayLength: case ExpressionType.Convert: case ExpressionType.ConvertChecked: case ExpressionType.Negate: case ExpressionType.NegateChecked: case ExpressionType.Not: case ExpressionType.Quote: case ExpressionType.TypeAs: case ExpressionType.UnaryPlus: { var e1 = (UnaryExpression)expr1; var e2 = (UnaryExpression)expr2; return e1.Method == e2.Method && Compare(e1.Operand, e2.Operand, visited, queryableAccessorDic); } case ExpressionType.Call: { var e1 = (MethodCallExpression)expr1; var e2 = (MethodCallExpression)expr2; if (e1.Arguments.Count != e2.Arguments.Count || e1.Method != e2.Method) return false; if (queryableAccessorDic.Count > 0) { QueryableAccessor qa; if (queryableAccessorDic.TryGetValue(expr1, out qa)) return Compare(qa.Queryable.Expression, qa.Accessor(expr2).Expression, visited, queryableAccessorDic); } if (!Compare(e1.Object, e2.Object, visited, queryableAccessorDic)) return false; for (var i = 0; i < e1.Arguments.Count; i++) if (!Compare(e1.Arguments[i], e2.Arguments[i], visited, queryableAccessorDic)) return false; return true; } case ExpressionType.Conditional: { var e1 = (ConditionalExpression)expr1; var e2 = (ConditionalExpression)expr2; return Compare(e1.Test, e2.Test, visited, queryableAccessorDic) && Compare(e1.IfTrue, e2.IfTrue, visited, queryableAccessorDic) && Compare(e1.IfFalse, e2.IfFalse, visited, queryableAccessorDic); } case ExpressionType.Constant: { var e1 = (ConstantExpression)expr1; var e2 = (ConstantExpression)expr2; if (e1.Value == null && e2.Value == null) return true; if (IsConstant(e1.Type)) return Equals(e1.Value, e2.Value); if (e1.Value == null || e2.Value == null) return false; if (e1.Value is IQueryable) { var eq1 = ((IQueryable)e1.Value).Expression; var eq2 = ((IQueryable)e2.Value).Expression; if (visited.Add(eq1)) return Compare(eq1, eq2, visited, queryableAccessorDic); } return true; } case ExpressionType.Invoke: { var e1 = (InvocationExpression)expr1; var e2 = (InvocationExpression)expr2; if (e1.Arguments.Count != e2.Arguments.Count || !Compare(e1.Expression, e2.Expression, visited, queryableAccessorDic)) return false; for (var i = 0; i < e1.Arguments.Count; i++) if (!Compare(e1.Arguments[i], e2.Arguments[i], visited, queryableAccessorDic)) return false; return true; } case ExpressionType.Lambda: { var e1 = (LambdaExpression)expr1; var e2 = (LambdaExpression)expr2; if (e1.Parameters.Count != e2.Parameters.Count || !Compare(e1.Body, e2.Body, visited, queryableAccessorDic)) return false; for (var i = 0; i < e1.Parameters.Count; i++) if (!Compare(e1.Parameters[i], e2.Parameters[i], visited, queryableAccessorDic)) return false; return true; } case ExpressionType.ListInit: { var e1 = (ListInitExpression)expr1; var e2 = (ListInitExpression)expr2; if (e1.Initializers.Count != e2.Initializers.Count || !Compare(e1.NewExpression, e2.NewExpression, visited, queryableAccessorDic)) return false; for (var i = 0; i < e1.Initializers.Count; i++) { var i1 = e1.Initializers[i]; var i2 = e2.Initializers[i]; if (i1.Arguments.Count != i2.Arguments.Count || i1.AddMethod != i2.AddMethod) return false; for (var j = 0; j < i1.Arguments.Count; j++) if (!Compare(i1.Arguments[j], i2.Arguments[j], visited, queryableAccessorDic)) return false; } return true; } case ExpressionType.MemberAccess: { var e1 = (MemberExpression)expr1; var e2 = (MemberExpression)expr2; if (e1.Member == e2.Member) { if (e1.Expression == e2.Expression || e1.Expression.Type == e2.Expression.Type) { if (queryableAccessorDic.Count > 0) { QueryableAccessor qa; if (queryableAccessorDic.TryGetValue(expr1, out qa)) return Compare(e1.Expression, e2.Expression, visited, queryableAccessorDic) && Compare(qa.Queryable.Expression, qa.Accessor(expr2).Expression, visited, queryableAccessorDic); } } return Compare(e1.Expression, e2.Expression, visited, queryableAccessorDic); } return false; } case ExpressionType.MemberInit: { var e1 = (MemberInitExpression)expr1; var e2 = (MemberInitExpression)expr2; if (e1.Bindings.Count != e2.Bindings.Count || !Compare(e1.NewExpression, e2.NewExpression, visited, queryableAccessorDic)) return false; Func<MemberBinding,MemberBinding,bool> compareBindings = null; compareBindings = (b1,b2) => { if (b1 == b2) return true; if (b1 == null || b2 == null || b1.BindingType != b2.BindingType || b1.Member != b2.Member) return false; switch (b1.BindingType) { case MemberBindingType.Assignment: return Compare(((MemberAssignment)b1).Expression, ((MemberAssignment)b2).Expression, visited, queryableAccessorDic); case MemberBindingType.ListBinding: var ml1 = (MemberListBinding)b1; var ml2 = (MemberListBinding)b2; if (ml1.Initializers.Count != ml2.Initializers.Count) return false; for (var i = 0; i < ml1.Initializers.Count; i++) { var ei1 = ml1.Initializers[i]; var ei2 = ml2.Initializers[i]; if (ei1.AddMethod != ei2.AddMethod || ei1.Arguments.Count != ei2.Arguments.Count) return false; for (var j = 0; j < ei1.Arguments.Count; j++) if (!Compare(ei1.Arguments[j], ei2.Arguments[j], visited, queryableAccessorDic)) return false; } break; case MemberBindingType.MemberBinding: var mm1 = (MemberMemberBinding)b1; var mm2 = (MemberMemberBinding)b2; if (mm1.Bindings.Count != mm2.Bindings.Count) return false; for (var i = 0; i < mm1.Bindings.Count; i++) if (!compareBindings(mm1.Bindings[i], mm2.Bindings[i])) return false; break; } return true; }; for (var i = 0; i < e1.Bindings.Count; i++) { var b1 = e1.Bindings[i]; var b2 = e2.Bindings[i]; if (!compareBindings(b1, b2)) return false; } return true; } case ExpressionType.New: { var e1 = (NewExpression)expr1; var e2 = (NewExpression)expr2; if (e1.Arguments.Count != e2.Arguments.Count) return false; if (e1.Members == null && e2.Members != null) return false; if (e1.Members != null && e2.Members == null) return false; if (e1.Constructor != e2.Constructor) return false; if (e1.Members != null) { if (e1.Members.Count != e2.Members.Count) return false; for (var i = 0; i < e1.Members.Count; i++) if (e1.Members[i] != e2.Members[i]) return false; } for (var i = 0; i < e1.Arguments.Count; i++) if (!Compare(e1.Arguments[i], e2.Arguments[i], visited, queryableAccessorDic)) return false; return true; } case ExpressionType.NewArrayBounds: case ExpressionType.NewArrayInit: { var e1 = (NewArrayExpression)expr1; var e2 = (NewArrayExpression)expr2; if (e1.Expressions.Count != e2.Expressions.Count) return false; for (var i = 0; i < e1.Expressions.Count; i++) if (!Compare(e1.Expressions[i], e2.Expressions[i], visited, queryableAccessorDic)) return false; return true; } case ExpressionType.Parameter: { var e1 = (ParameterExpression)expr1; var e2 = (ParameterExpression)expr2; return e1.Name == e2.Name; } case ExpressionType.TypeIs: { var e1 = (TypeBinaryExpression)expr1; var e2 = (TypeBinaryExpression)expr2; return e1.TypeOperand == e2.TypeOperand && Compare(e1.Expression, e2.Expression, visited, queryableAccessorDic); } #if FW4 || SILVERLIGHT case ExpressionType.Block: { var e1 = (BlockExpression)expr1; var e2 = (BlockExpression)expr2; for (var i = 0; i < e1.Expressions.Count; i++) if (!Compare(e1.Expressions[i], e2.Expressions[i], visited, queryableAccessorDic)) return false; for (var i = 0; i < e1.Variables.Count; i++) if (!Compare(e1.Variables[i], e2.Variables[i], visited, queryableAccessorDic)) return false; return true; } #endif } throw new InvalidOperationException(); } #endregion #region Path static Expression ConvertTo(Expression expr, Type type) { return Expression.Convert(expr, type); } static void Path<T>(IEnumerable<T> source, HashSet<Expression> visited, Expression path, MethodInfo property, Action<T,Expression> func) where T : class { var prop = Expression.Property(path, property); var i = 0; foreach (var item in source) func(item, Expression.Call(prop, ReflectionHelper.IndexExpressor<T>.Item, new Expression[] { Expression.Constant(i++) })); } static void Path<T>(IEnumerable<T> source, HashSet<Expression> visited, Expression path, MethodInfo property, Action<Expression,Expression> func) where T : Expression { var prop = Expression.Property(path, property); var i = 0; foreach (var item in source) Path(item, visited, Expression.Call(prop, ReflectionHelper.IndexExpressor<T>.Item, new Expression[] { Expression.Constant(i++) }), func); } static void Path(Expression expr, HashSet<Expression> visited, Expression path, MethodInfo property, Action<Expression,Expression> func) { Path(expr, visited, Expression.Property(path, property), func); } public static void Path(this Expression expr, Expression path, Action<Expression,Expression> func) { Path(expr, new HashSet<Expression>(), path, func); } static void Path( this Expression expr, HashSet<Expression> visited, Expression path, Action<Expression,Expression> func) { if (expr == null) return; switch (expr.NodeType) { case ExpressionType.Add: case ExpressionType.AddChecked: case ExpressionType.And: case ExpressionType.AndAlso: case ExpressionType.ArrayIndex: #if FW4 || SILVERLIGHT case ExpressionType.Assign: #endif case ExpressionType.Coalesce: case ExpressionType.Divide: case ExpressionType.Equal: case ExpressionType.ExclusiveOr: case ExpressionType.GreaterThan: case ExpressionType.GreaterThanOrEqual: case ExpressionType.LeftShift: case ExpressionType.LessThan: case ExpressionType.LessThanOrEqual: case ExpressionType.Modulo: case ExpressionType.Multiply: case ExpressionType.MultiplyChecked: case ExpressionType.NotEqual: case ExpressionType.Or: case ExpressionType.OrElse: case ExpressionType.Power: case ExpressionType.RightShift: case ExpressionType.Subtract: case ExpressionType.SubtractChecked: { path = ConvertTo(path, typeof(BinaryExpression)); var e = (BinaryExpression)expr; Path(e.Conversion, visited, path, ReflectionHelper.Binary.Conversion, func); Path(e.Left, visited, path, ReflectionHelper.Binary.Left, func); Path(e.Right, visited, path, ReflectionHelper.Binary.Right, func); break; } case ExpressionType.ArrayLength: case ExpressionType.Convert: case ExpressionType.ConvertChecked: case ExpressionType.Negate: case ExpressionType.NegateChecked: case ExpressionType.Not: case ExpressionType.Quote: case ExpressionType.TypeAs: case ExpressionType.UnaryPlus: Path( ((UnaryExpression)expr).Operand, visited, path = ConvertTo(path, typeof(UnaryExpression)), ReflectionHelper.Unary.Operand, func); break; case ExpressionType.Call: { path = ConvertTo(path, typeof(MethodCallExpression)); var e = (MethodCallExpression)expr; Path(e.Object, visited, path, ReflectionHelper.MethodCall.Object, func); Path(e.Arguments, visited, path, ReflectionHelper.MethodCall.Arguments, func); break; } case ExpressionType.Conditional: { path = ConvertTo(path, typeof(ConditionalExpression)); var e = (ConditionalExpression)expr; Path(e.Test, visited, path, ReflectionHelper.Conditional.Test, func); Path(e.IfTrue, visited, path, ReflectionHelper.Conditional.IfTrue, func); Path(e.IfFalse, visited, path, ReflectionHelper.Conditional.IfFalse, func); break; } case ExpressionType.Invoke: { path = ConvertTo(path, typeof(InvocationExpression)); var e = (InvocationExpression)expr; Path(e.Expression, visited, path, ReflectionHelper.Invocation.Expression, func); Path(e.Arguments, visited, path, ReflectionHelper.Invocation.Arguments, func); break; } case ExpressionType.Lambda: { path = ConvertTo(path, typeof(LambdaExpression)); var e = (LambdaExpression)expr; Path(e.Body, visited, path, ReflectionHelper.LambdaExpr.Body, func); Path(e.Parameters, visited, path, ReflectionHelper.LambdaExpr.Parameters, func); break; } case ExpressionType.ListInit: { path = ConvertTo(path, typeof(ListInitExpression)); var e = (ListInitExpression)expr; Path(e.NewExpression, visited, path, ReflectionHelper.ListInit.NewExpression, func); Path(e.Initializers, visited, path, ReflectionHelper.ListInit.Initializers, (ex,p) => Path(ex.Arguments, visited, p, ReflectionHelper.ElementInit.Arguments, func)); break; } case ExpressionType.MemberAccess: Path( ((MemberExpression)expr).Expression, visited, path = ConvertTo(path, typeof(MemberExpression)), ReflectionHelper.Member.Expression, func); break; case ExpressionType.MemberInit: { Action<MemberBinding,Expression> modify = null; modify = (b,pinf) => { switch (b.BindingType) { case MemberBindingType.Assignment: Path( ((MemberAssignment)b).Expression, visited, ConvertTo(pinf, typeof(MemberAssignment)), ReflectionHelper.MemberAssignmentBind.Expression, func); break; case MemberBindingType.ListBinding: Path( ((MemberListBinding)b).Initializers, visited, ConvertTo(pinf, typeof(MemberListBinding)), ReflectionHelper.MemberListBind.Initializers, (p,psi) => Path(p.Arguments, visited, psi, ReflectionHelper.ElementInit.Arguments, func)); break; case MemberBindingType.MemberBinding: Path( ((MemberMemberBinding)b).Bindings, visited, ConvertTo(pinf, typeof(MemberMemberBinding)), ReflectionHelper.MemberMemberBind.Bindings, modify); break; } }; path = ConvertTo(path, typeof(MemberInitExpression)); var e = (MemberInitExpression)expr; Path(e.NewExpression, visited, path, ReflectionHelper.MemberInit.NewExpression, func); Path(e.Bindings, visited, path, ReflectionHelper.MemberInit.Bindings, modify); break; } case ExpressionType.New: Path( ((NewExpression)expr).Arguments, visited, path = ConvertTo(path, typeof(NewExpression)), ReflectionHelper.New.Arguments, func); break; case ExpressionType.NewArrayBounds: Path( ((NewArrayExpression)expr).Expressions, visited, path = ConvertTo(path, typeof(NewArrayExpression)), ReflectionHelper.NewArray.Expressions, func); break; case ExpressionType.NewArrayInit: Path( ((NewArrayExpression)expr).Expressions, visited, path = ConvertTo(path, typeof(NewArrayExpression)), ReflectionHelper.NewArray.Expressions, func); break; case ExpressionType.TypeIs: Path( ((TypeBinaryExpression)expr).Expression, visited, path = ConvertTo(path, typeof(TypeBinaryExpression)), ReflectionHelper.TypeBinary.Expression, func); break; #if FW4 || SILVERLIGHT case ExpressionType.Block: { path = ConvertTo(path, typeof(BlockExpression)); var e = (BlockExpression)expr; Path(e.Expressions, visited, path, ReflectionHelper.Block.Expressions, func); Path(e.Variables, visited, path, ReflectionHelper.Block.Variables, func); // ? break; } #endif case ExpressionType.Constant : { path = ConvertTo(path, typeof(ConstantExpression)); var e = (ConstantExpression)expr; var iq = e.Value as IQueryable; if (iq != null && !visited.Contains(iq.Expression)) { visited.Add(iq.Expression); Expression p = Expression.Property(path, ReflectionHelper.Constant.Value); p = ConvertTo(p, typeof(IQueryable)); Path(iq.Expression, visited, p, ReflectionHelper.QueryableInt.Expression, func); } break; } case ExpressionType.Parameter: path = ConvertTo(path, typeof(ParameterExpression)); break; } func(expr, path); } #endregion #region Visit static void Visit<T>(IEnumerable<T> source, Action<T> func) { foreach (var item in source) func(item); } static void Visit<T>(IEnumerable<T> source, Action<Expression> func) where T : Expression { foreach (var item in source) Visit(item, func); } public static void Visit(this Expression expr, Action<Expression> func) { if (expr == null) return; switch (expr.NodeType) { case ExpressionType.Add: case ExpressionType.AddChecked: case ExpressionType.And: case ExpressionType.AndAlso: case ExpressionType.ArrayIndex: #if FW4 || SILVERLIGHT case ExpressionType.Assign: #endif case ExpressionType.Coalesce: case ExpressionType.Divide: case ExpressionType.Equal: case ExpressionType.ExclusiveOr: case ExpressionType.GreaterThan: case ExpressionType.GreaterThanOrEqual: case ExpressionType.LeftShift: case ExpressionType.LessThan: case ExpressionType.LessThanOrEqual: case ExpressionType.Modulo: case ExpressionType.Multiply: case ExpressionType.MultiplyChecked: case ExpressionType.NotEqual: case ExpressionType.Or: case ExpressionType.OrElse: case ExpressionType.Power: case ExpressionType.RightShift: case ExpressionType.Subtract: case ExpressionType.SubtractChecked: { var e = (BinaryExpression)expr; Visit(e.Conversion, func); Visit(e.Left, func); Visit(e.Right, func); break; } case ExpressionType.ArrayLength: case ExpressionType.Convert: case ExpressionType.ConvertChecked: case ExpressionType.Negate: case ExpressionType.NegateChecked: case ExpressionType.Not: case ExpressionType.Quote: case ExpressionType.TypeAs: case ExpressionType.UnaryPlus: Visit(((UnaryExpression)expr).Operand, func); break; case ExpressionType.Call: { var e = (MethodCallExpression)expr; Visit(e.Object, func); Visit(e.Arguments, func); break; } case ExpressionType.Conditional: { var e = (ConditionalExpression)expr; Visit(e.Test, func); Visit(e.IfTrue, func); Visit(e.IfFalse, func); break; } case ExpressionType.Invoke: { var e = (InvocationExpression)expr; Visit(e.Expression, func); Visit(e.Arguments, func); break; } case ExpressionType.Lambda: { var e = (LambdaExpression)expr; Visit(e.Body, func); Visit(e.Parameters, func); break; } case ExpressionType.ListInit: { var e = (ListInitExpression)expr; Visit(e.NewExpression, func); Visit(e.Initializers, ex => Visit(ex.Arguments, func)); break; } case ExpressionType.MemberAccess: Visit(((MemberExpression)expr).Expression, func); break; case ExpressionType.MemberInit: { Action<MemberBinding> modify = null; modify = b => { switch (b.BindingType) { case MemberBindingType.Assignment : Visit(((MemberAssignment)b). Expression, func); break; case MemberBindingType.ListBinding : Visit(((MemberListBinding)b).Initializers, p => Visit(p.Arguments, func)); break; case MemberBindingType.MemberBinding : Visit(((MemberMemberBinding)b).Bindings, modify); break; } }; var e = (MemberInitExpression)expr; Visit(e.NewExpression, func); Visit(e.Bindings, modify); break; } case ExpressionType.New : Visit(((NewExpression) expr).Arguments, func); break; case ExpressionType.NewArrayBounds : Visit(((NewArrayExpression) expr).Expressions, func); break; case ExpressionType.NewArrayInit : Visit(((NewArrayExpression) expr).Expressions, func); break; case ExpressionType.TypeIs : Visit(((TypeBinaryExpression)expr).Expression, func); break; #if FW4 || SILVERLIGHT case ExpressionType.Block: { var e = (BlockExpression)expr; Visit(e.Expressions, func); Visit(e.Variables, func); break; } #endif case (ExpressionType)ChangeTypeExpression.ChangeTypeType : Visit(((ChangeTypeExpression)expr).Expression, func); break; } func(expr); } static void Visit<T>(IEnumerable<T> source, Func<T,bool> func) { foreach (var item in source) func(item); } static void Visit<T>(IEnumerable<T> source, Func<Expression,bool> func) where T : Expression { foreach (var item in source) Visit(item, func); } public static void Visit(this Expression expr, Func<Expression,bool> func) { if (expr == null || !func(expr)) return; switch (expr.NodeType) { case ExpressionType.Add: case ExpressionType.AddChecked: case ExpressionType.And: case ExpressionType.AndAlso: case ExpressionType.ArrayIndex: #if FW4 || SILVERLIGHT case ExpressionType.Assign: #endif case ExpressionType.Coalesce: case ExpressionType.Divide: case ExpressionType.Equal: case ExpressionType.ExclusiveOr: case ExpressionType.GreaterThan: case ExpressionType.GreaterThanOrEqual: case ExpressionType.LeftShift: case ExpressionType.LessThan: case ExpressionType.LessThanOrEqual: case ExpressionType.Modulo: case ExpressionType.Multiply: case ExpressionType.MultiplyChecked: case ExpressionType.NotEqual: case ExpressionType.Or: case ExpressionType.OrElse: case ExpressionType.Power: case ExpressionType.RightShift: case ExpressionType.Subtract: case ExpressionType.SubtractChecked: { var e = (BinaryExpression)expr; Visit(e.Conversion, func); Visit(e.Left, func); Visit(e.Right, func); break; } case ExpressionType.ArrayLength: case ExpressionType.Convert: case ExpressionType.ConvertChecked: case ExpressionType.Negate: case ExpressionType.NegateChecked: case ExpressionType.Not: case ExpressionType.Quote: case ExpressionType.TypeAs: case ExpressionType.UnaryPlus: Visit(((UnaryExpression)expr).Operand, func); break; case ExpressionType.Call: { var e = (MethodCallExpression)expr; Visit(e.Object, func); Visit(e.Arguments, func); break; } case ExpressionType.Conditional: { var e = (ConditionalExpression)expr; Visit(e.Test, func); Visit(e.IfTrue, func); Visit(e.IfFalse, func); break; } case ExpressionType.Invoke: { var e = (InvocationExpression)expr; Visit(e.Expression, func); Visit(e.Arguments, func); break; } case ExpressionType.Lambda: { var e = (LambdaExpression)expr; Visit(e.Body, func); Visit(e.Parameters, func); break; } case ExpressionType.ListInit: { var e = (ListInitExpression)expr; Visit(e.NewExpression, func); Visit(e.Initializers, ex => Visit(ex.Arguments, func)); break; } case ExpressionType.MemberAccess: Visit(((MemberExpression)expr).Expression, func); break; case ExpressionType.MemberInit: { Func<MemberBinding,bool> modify = null; modify = b => { switch (b.BindingType) { case MemberBindingType.Assignment : Visit(((MemberAssignment)b). Expression, func); break; case MemberBindingType.ListBinding : Visit(((MemberListBinding)b).Initializers, p => Visit(p.Arguments, func)); break; case MemberBindingType.MemberBinding : Visit(((MemberMemberBinding)b).Bindings, modify); break; } return true; }; var e = (MemberInitExpression)expr; Visit(e.NewExpression, func); Visit(e.Bindings, modify); break; } case ExpressionType.New : Visit(((NewExpression) expr).Arguments, func); break; case ExpressionType.NewArrayBounds : Visit(((NewArrayExpression) expr).Expressions, func); break; case ExpressionType.NewArrayInit : Visit(((NewArrayExpression) expr).Expressions, func); break; case ExpressionType.TypeIs : Visit(((TypeBinaryExpression)expr).Expression, func); break; #if FW4 || SILVERLIGHT case ExpressionType.Block: { var e = (BlockExpression)expr; Visit(e.Expressions, func); Visit(e.Variables, func); break; } #endif case (ExpressionType)ChangeTypeExpression.ChangeTypeType : Visit(((ChangeTypeExpression)expr).Expression, func); break; } } #endregion #region Find static Expression Find<T>(IEnumerable<T> source, Func<T,Expression> func) { foreach (var item in source) { var ex = func(item); if (ex != null) return ex; } return null; } static Expression Find<T>(IEnumerable<T> source, Func<Expression,bool> func) where T : Expression { foreach (var item in source) { var f = Find(item, func); if (f != null) return f; } return null; } public static Expression Find(this Expression expr, Func<Expression,bool> func) { if (expr == null || func(expr)) return expr; switch (expr.NodeType) { case ExpressionType.Add: case ExpressionType.AddChecked: case ExpressionType.And: case ExpressionType.AndAlso: case ExpressionType.ArrayIndex: #if FW4 || SILVERLIGHT case ExpressionType.Assign: #endif case ExpressionType.Coalesce: case ExpressionType.Divide: case ExpressionType.Equal: case ExpressionType.ExclusiveOr: case ExpressionType.GreaterThan: case ExpressionType.GreaterThanOrEqual: case ExpressionType.LeftShift: case ExpressionType.LessThan: case ExpressionType.LessThanOrEqual: case ExpressionType.Modulo: case ExpressionType.Multiply: case ExpressionType.MultiplyChecked: case ExpressionType.NotEqual: case ExpressionType.Or: case ExpressionType.OrElse: case ExpressionType.Power: case ExpressionType.RightShift: case ExpressionType.Subtract: case ExpressionType.SubtractChecked: { var e = (BinaryExpression)expr; return Find(e.Conversion, func) ?? Find(e.Left, func) ?? Find(e.Right, func); } case ExpressionType.ArrayLength: case ExpressionType.Convert: case ExpressionType.ConvertChecked: case ExpressionType.Negate: case ExpressionType.NegateChecked: case ExpressionType.Not: case ExpressionType.Quote: case ExpressionType.TypeAs: case ExpressionType.UnaryPlus: return Find(((UnaryExpression)expr).Operand, func); case ExpressionType.Call: { var e = (MethodCallExpression)expr; return Find(e.Object, func) ?? Find(e.Arguments, func); } case ExpressionType.Conditional: { var e = (ConditionalExpression)expr; return Find(e.Test, func) ?? Find(e.IfTrue, func) ?? Find(e.IfFalse, func); } case ExpressionType.Invoke: { var e = (InvocationExpression)expr; return Find(e.Expression, func) ?? Find(e.Arguments, func); } case ExpressionType.Lambda: { var e = (LambdaExpression)expr; return Find(e.Body, func) ?? Find(e.Parameters, func); } case ExpressionType.ListInit: { var e = (ListInitExpression)expr; return Find(e.NewExpression, func) ?? Find(e.Initializers, ex => Find(ex.Arguments, func)); } case ExpressionType.MemberAccess: return Find(((MemberExpression)expr).Expression, func); case ExpressionType.MemberInit: { Func<MemberBinding,Expression> modify = null; modify = b => { switch (b.BindingType) { case MemberBindingType.Assignment : return Find(((MemberAssignment)b). Expression, func); case MemberBindingType.ListBinding : return Find(((MemberListBinding)b). Initializers, p => Find(p.Arguments, func)); case MemberBindingType.MemberBinding : return Find(((MemberMemberBinding)b).Bindings, modify); } return null; }; var e = (MemberInitExpression)expr; return Find(e.NewExpression, func) ?? Find(e.Bindings, modify); } case ExpressionType.New : return Find(((NewExpression) expr).Arguments, func); case ExpressionType.NewArrayBounds : return Find(((NewArrayExpression) expr).Expressions, func); case ExpressionType.NewArrayInit : return Find(((NewArrayExpression) expr).Expressions, func); case ExpressionType.TypeIs : return Find(((TypeBinaryExpression)expr).Expression, func); #if FW4 || SILVERLIGHT case ExpressionType.Block: { var e = (BlockExpression)expr; return Find(e.Expressions, func) ?? Find(e.Variables, func); } #endif case (ExpressionType)ChangeTypeExpression.ChangeTypeType : return Find(((ChangeTypeExpression)expr).Expression, func); } return null; } #endregion #region Convert static IEnumerable<T> Convert<T>(IEnumerable<T> source, Func<T,T> func) where T : class { var modified = false; var list = new List<T>(); foreach (var item in source) { var e = func(item); list.Add(e); modified = modified || e != item; } return modified ? list : source; } static IEnumerable<T> Convert<T>(IEnumerable<T> source, Func<Expression,Expression> func) where T : Expression { var modified = false; var list = new List<T>(); foreach (var item in source) { var e = Convert(item, func); list.Add((T)e); modified = modified || e != item; } return modified? list: source; } public static Expression Convert(this Expression expr, Func<Expression,Expression> func) { if (expr == null) return null; switch (expr.NodeType) { case ExpressionType.Add: case ExpressionType.AddChecked: case ExpressionType.And: case ExpressionType.AndAlso: case ExpressionType.ArrayIndex: #if FW4 || SILVERLIGHT case ExpressionType.Assign: #endif case ExpressionType.Coalesce: case ExpressionType.Divide: case ExpressionType.Equal: case ExpressionType.ExclusiveOr: case ExpressionType.GreaterThan: case ExpressionType.GreaterThanOrEqual: case ExpressionType.LeftShift: case ExpressionType.LessThan: case ExpressionType.LessThanOrEqual: case ExpressionType.Modulo: case ExpressionType.Multiply: case ExpressionType.MultiplyChecked: case ExpressionType.NotEqual: case ExpressionType.Or: case ExpressionType.OrElse: case ExpressionType.Power: case ExpressionType.RightShift: case ExpressionType.Subtract: case ExpressionType.SubtractChecked: { var ex = func(expr); if (ex != expr) return ex; var e = (BinaryExpression)expr; var c = Convert(e.Conversion, func); var l = Convert(e.Left, func); var r = Convert(e.Right, func); #if FW3 return c != e.Conversion || l != e.Left || r != e.Right ? Expression.MakeBinary(expr.NodeType, l, r, e.IsLiftedToNull, e.Method, (LambdaExpression)c): expr; #else return e.Update(l, (LambdaExpression)c, r); #endif } case ExpressionType.ArrayLength: case ExpressionType.Convert: case ExpressionType.ConvertChecked: case ExpressionType.Negate: case ExpressionType.NegateChecked: case ExpressionType.Not: case ExpressionType.Quote: case ExpressionType.TypeAs: case ExpressionType.UnaryPlus: { var ex = func(expr); if (ex != expr) return ex; var e = (UnaryExpression)expr; var o = Convert(e.Operand, func); #if FW3 return o != e.Operand ? Expression.MakeUnary(expr.NodeType, o, e.Type, e.Method) : expr; #else return e.Update(o); #endif } case ExpressionType.Call: { var ex = func(expr); if (ex != expr) return ex; var e = (MethodCallExpression)expr; var o = Convert(e.Object, func); var a = Convert(e.Arguments, func); #if FW3 return o != e.Object || a != e.Arguments ? Expression.Call(o, e.Method, a) : expr; #else return e.Update(o, a); #endif } case ExpressionType.Conditional: { var ex = func(expr); if (ex != expr) return ex; var e = (ConditionalExpression)expr; var s = Convert(e.Test, func); var t = Convert(e.IfTrue, func); var f = Convert(e.IfFalse, func); #if FW3 return s != e.Test || t != e.IfTrue || f != e.IfFalse ? Expression.Condition(s, t, f) : expr; #else return e.Update(s, t, f); #endif } case ExpressionType.Invoke: { var exp = func(expr); if (exp != expr) return exp; var e = (InvocationExpression)expr; var ex = Convert(e.Expression, func); var a = Convert(e.Arguments, func); #if FW3 return ex != e.Expression || a != e.Arguments ? Expression.Invoke(ex, a) : expr; #else return e.Update(ex, a); #endif } case ExpressionType.Lambda: { var ex = func(expr); if (ex != expr) return ex; var e = (LambdaExpression)expr; var b = Convert(e.Body, func); var p = Convert(e.Parameters, func); return b != e.Body || p != e.Parameters ? Expression.Lambda(ex.Type, b, p.ToArray()) : expr; } case ExpressionType.ListInit: { var ex = func(expr); if (ex != expr) return ex; var e = (ListInitExpression)expr; var n = Convert(e.NewExpression, func); var i = Convert(e.Initializers, p => { var args = Convert(p.Arguments, func); return args != p.Arguments? Expression.ElementInit(p.AddMethod, args): p; }); #if FW3 return n != e.NewExpression || i != e.Initializers ? Expression.ListInit((NewExpression)n, i) : expr; #else return e.Update((NewExpression)n, i); #endif } case ExpressionType.MemberAccess: { var exp = func(expr); if (exp != expr) return exp; var e = (MemberExpression)expr; var ex = Convert(e.Expression, func); #if FW3 return ex != e.Expression ? Expression.MakeMemberAccess(ex, e.Member) : expr; #else return e.Update(ex); #endif } case ExpressionType.MemberInit: { var exp = func(expr); if (exp != expr) return exp; Func<MemberBinding,MemberBinding> modify = null; modify = b => { switch (b.BindingType) { case MemberBindingType.Assignment: { var ma = (MemberAssignment)b; var ex = Convert(ma.Expression, func); if (ex != ma.Expression) ma = Expression.Bind(ma.Member, ex); return ma; } case MemberBindingType.ListBinding: { var ml = (MemberListBinding)b; var i = Convert(ml.Initializers, p => { var args = Convert(p.Arguments, func); return args != p.Arguments? Expression.ElementInit(p.AddMethod, args): p; }); if (i != ml.Initializers) ml = Expression.ListBind(ml.Member, i); return ml; } case MemberBindingType.MemberBinding: { var mm = (MemberMemberBinding)b; var bs = Convert(mm.Bindings, modify); if (bs != mm.Bindings) mm = Expression.MemberBind(mm.Member); return mm; } } return b; }; var e = (MemberInitExpression)expr; var ne = Convert(e.NewExpression, func); var bb = Convert(e.Bindings, modify); #if FW3 return ne != e.NewExpression || bb != e.Bindings ? Expression.MemberInit((NewExpression)ne, bb) : expr; #else return e.Update((NewExpression)ne, bb); #endif } case ExpressionType.New: { var ex = func(expr); if (ex != expr) return ex; var e = (NewExpression)expr; var a = Convert(e.Arguments, func); #if FW3 return a != e.Arguments ? e.Members == null ? Expression.New(e.Constructor, a) : Expression.New(e.Constructor, a, e.Members) : expr; #else return e.Update(a); #endif } case ExpressionType.NewArrayBounds: { var exp = func(expr); if (exp != expr) return exp; var e = (NewArrayExpression)expr; var ex = Convert(e.Expressions, func); #if FW3 return ex != e.Expressions ? Expression.NewArrayBounds(e.Type, ex) : expr; #else return e.Update(ex); #endif } case ExpressionType.NewArrayInit: { var exp = func(expr); if (exp != expr) return exp; var e = (NewArrayExpression)expr; var ex = Convert(e.Expressions, func); #if FW3 return ex != e.Expressions ? Expression.NewArrayInit(e.Type.GetElementType(), ex) : expr; #else return e.Update(ex); #endif } case ExpressionType.TypeIs: { var exp = func(expr); if (exp != expr) return exp; var e = (TypeBinaryExpression)expr; var ex = Convert(e.Expression, func); #if FW3 return ex != e.Expression ? Expression.TypeIs(ex, e.Type) : expr; #else return e.Update(ex); #endif } #if FW4 || SILVERLIGHT case ExpressionType.Block: { var exp = func(expr); if (exp != expr) return exp; var e = (BlockExpression)expr; var ex = Convert(e.Expressions, func); var v = Convert(e.Variables, func); return e.Update(v, ex); } #endif case ExpressionType.Constant : return func(expr); case ExpressionType.Parameter: return func(expr); case (ExpressionType)ChangeTypeExpression.ChangeTypeType : { var exp = func(expr); if (exp != expr) return exp; var e = expr as ChangeTypeExpression; var ex = Convert(e.Expression, func); if (ex == e.Expression) return expr; if (ex.Type == e.Type) return ex; return new ChangeTypeExpression(ex, e.Type); } } throw new InvalidOperationException(); } #endregion #region Convert2 public struct ConvertInfo { public ConvertInfo(Expression expression, bool stop) { Expression = expression; Stop = stop; } public ConvertInfo(Expression expression) { Expression = expression; Stop = false; } public Expression Expression; public bool Stop; } static IEnumerable<T> Convert2<T>(IEnumerable<T> source, Func<T,T> func) where T : class { var modified = false; var list = new List<T>(); foreach (var item in source) { var e = func(item); list.Add(e); modified = modified || e != item; } return modified ? list : source; } static IEnumerable<T> Convert2<T>(IEnumerable<T> source, Func<Expression,ConvertInfo> func) where T : Expression { var modified = false; var list = new List<T>(); foreach (var item in source) { var e = Convert2(item, func); list.Add((T)e); modified = modified || e != item; } return modified ? list : source; } #if FW3 static IEnumerable<Expression> ConvertMethodArguments(IEnumerable<Expression> source, MethodBase method) { return ConvertMethodArguments(source, method, null); } static IEnumerable<Expression> ConvertMethodArguments(IEnumerable<Expression> source, MethodBase method, IList<MemberInfo> initMembers) #else static IEnumerable<Expression> ConvertMethodArguments(IEnumerable<Expression> source, MethodBase method, IList<MemberInfo> initMembers = null) #endif { var list = new List<Expression>(); var targetTypes = new List<Type>(); foreach (var param in method.GetParameters()) { targetTypes.Add(param.ParameterType); } if (initMembers != null) { foreach (var mi in initMembers) { if (mi is PropertyInfo) { targetTypes.Add(((PropertyInfo)mi).PropertyType); } else if (mi is FieldInfo) { targetTypes.Add(((FieldInfo)mi).FieldType); } } } var idx = 0; foreach (var item in source) { var targetType = targetTypes[idx]; if (item.Type != targetType) { list.Add(Expression.Convert(item, targetType)); } else { list.Add(item); } idx++; } return list; } public static Expression Convert2(this Expression expr, Func<Expression,ConvertInfo> func) { if (expr == null) return null; ConvertInfo ci; switch (expr.NodeType) { case ExpressionType.Add: case ExpressionType.AddChecked: case ExpressionType.And: case ExpressionType.AndAlso: case ExpressionType.ArrayIndex: #if FW4 || SILVERLIGHT case ExpressionType.Assign: #endif case ExpressionType.Coalesce: case ExpressionType.Divide: case ExpressionType.Equal: case ExpressionType.ExclusiveOr: case ExpressionType.GreaterThan: case ExpressionType.GreaterThanOrEqual: case ExpressionType.LeftShift: case ExpressionType.LessThan: case ExpressionType.LessThanOrEqual: case ExpressionType.Modulo: case ExpressionType.Multiply: case ExpressionType.MultiplyChecked: case ExpressionType.NotEqual: case ExpressionType.Or: case ExpressionType.OrElse: case ExpressionType.Power: case ExpressionType.RightShift: case ExpressionType.Subtract: case ExpressionType.SubtractChecked: { ci = func(expr); if (ci.Stop || ci.Expression != expr) return ci.Expression; var e = expr as BinaryExpression; var c = Convert2(e.Conversion, func); var l = Convert2(e.Left, func); var r = Convert2(e.Right, func); return c != e.Conversion || l != e.Left || r != e.Right ? Expression.MakeBinary(expr.NodeType, l, r, e.IsLiftedToNull, e.Method, (LambdaExpression)c): expr; } case ExpressionType.ArrayLength: case ExpressionType.Convert: case ExpressionType.ConvertChecked: case ExpressionType.Negate: case ExpressionType.NegateChecked: case ExpressionType.Not: case ExpressionType.Quote: case ExpressionType.TypeAs: case ExpressionType.UnaryPlus: { ci = func(expr); if (ci.Stop || ci.Expression != expr) return ci.Expression; var e = expr as UnaryExpression; var o = Convert2(e.Operand, func); return o != e.Operand ? Expression.MakeUnary(expr.NodeType, o, e.Type, e.Method) : expr; } case ExpressionType.Call: { ci = func(expr); if (ci.Stop || ci.Expression != expr) return ci.Expression; var e = expr as MethodCallExpression; var o = Convert2(e.Object, func); var a = Convert2(e.Arguments, func); return o != e.Object || a != e.Arguments ? Expression.Call(o, e.Method, ConvertMethodArguments(a, e.Method)) : expr; } case ExpressionType.Conditional: { ci = func(expr); if (ci.Stop || ci.Expression != expr) return ci.Expression; var e = expr as ConditionalExpression; var s = Convert2(e.Test, func); var t = Convert2(e.IfTrue, func); var f = Convert2(e.IfFalse, func); return s != e.Test || t != e.IfTrue || f != e.IfFalse ? Expression.Condition(s, t, f) : expr; } case ExpressionType.Invoke: { ci = func(expr); if (ci.Stop || ci.Expression != expr) return ci.Expression; var e = expr as InvocationExpression; var ex = Convert2(e.Expression, func); var a = Convert2(e.Arguments, func); return ex != e.Expression || a != e.Arguments ? Expression.Invoke(ex, a) : expr; } case ExpressionType.Lambda: { ci = func(expr); if (ci.Stop || ci.Expression != expr) return ci.Expression; var e = expr as LambdaExpression; var b = Convert2(e.Body, func); var p = Convert2(e.Parameters, func); return b != e.Body || p != e.Parameters ? Expression.Lambda(ci.Expression.Type, b, p.ToArray()) : expr; } case ExpressionType.ListInit: { ci = func(expr); if (ci.Stop || ci.Expression != expr) return ci.Expression; var e = expr as ListInitExpression; var n = Convert2(e.NewExpression, func); var i = Convert2(e.Initializers, p => { var args = Convert2(p.Arguments, func); return args != p.Arguments? Expression.ElementInit(p.AddMethod, args): p; }); return n != e.NewExpression || i != e.Initializers ? Expression.ListInit((NewExpression)n, i) : expr; } case ExpressionType.MemberAccess: { ci = func(expr); if (ci.Stop || ci.Expression != expr) return ci.Expression; var e = expr as MemberExpression; var ex = Convert2(e.Expression, func); return ex != e.Expression ? Expression.MakeMemberAccess(ex, e.Member) : expr; } case ExpressionType.MemberInit: { ci = func(expr); if (ci.Stop || ci.Expression != expr) return ci.Expression; Func<MemberBinding,MemberBinding> modify = null; modify = b => { switch (b.BindingType) { case MemberBindingType.Assignment: { var ma = (MemberAssignment)b; var ex = Convert2(ma.Expression, func); if (ex != ma.Expression) { if (ex.Type != ma.Expression.Type) { ex = Expression.Convert(ex, ma.Expression.Type); } ma = Expression.Bind(ma.Member, ex); } return ma; } case MemberBindingType.ListBinding: { var ml = (MemberListBinding)b; var i = Convert(ml.Initializers, p => { var args = Convert2(p.Arguments, func); return args != p.Arguments? Expression.ElementInit(p.AddMethod, args): p; }); if (i != ml.Initializers) ml = Expression.ListBind(ml.Member, i); return ml; } case MemberBindingType.MemberBinding: { var mm = (MemberMemberBinding)b; var bs = Convert(mm.Bindings, modify); if (bs != mm.Bindings) mm = Expression.MemberBind(mm.Member); return mm; } } return b; }; var e = expr as MemberInitExpression; var ne = Convert2(e.NewExpression, func); var bb = Convert2(e.Bindings, modify); return ne != e.NewExpression || bb != e.Bindings ? Expression.MemberInit((NewExpression)ne, bb) : expr; } case ExpressionType.New: { ci = func(expr); if (ci.Stop || ci.Expression != expr) return ci.Expression; var e = expr as NewExpression; var a = Convert2(e.Arguments, func); return a != e.Arguments ? e.Members == null ? Expression.New(e.Constructor, ConvertMethodArguments(a, e.Constructor)) : Expression.New(e.Constructor, ConvertMethodArguments(a, e.Constructor, e.Members), e.Members) : expr; } case ExpressionType.NewArrayBounds: { ci = func(expr); if (ci.Stop || ci.Expression != expr) return ci.Expression; var e = expr as NewArrayExpression; var ex = Convert2(e.Expressions, func); return ex != e.Expressions ? Expression.NewArrayBounds(e.Type, ex) : expr; } case ExpressionType.NewArrayInit: { ci = func(expr); if (ci.Stop || ci.Expression != expr) return ci.Expression; var e = expr as NewArrayExpression; var et = e.Type.GetElementType(); var ex = Convert2(e.Expressions, func) .Select(ee => et == typeof(object) && ee.Type.IsValueType ? Expression.Convert(ee, typeof(object)) : ee); return ex != e.Expressions ? Expression.NewArrayInit(et, ex) : expr; } case ExpressionType.TypeIs : { ci = func(expr); if (ci.Stop || ci.Expression != expr) return ci.Expression; var e = expr as TypeBinaryExpression; var ex = Convert2(e.Expression, func); return ex != e.Expression ? Expression.TypeIs(ex, e.Type) : expr; } #if FW4 || SILVERLIGHT case ExpressionType.Block : { ci = func(expr); if (ci.Stop || ci.Expression != expr) return ci.Expression; var e = expr as BlockExpression; var ex = Convert2(e.Expressions, func); var v = Convert2(e.Variables, func); return ex != e.Expressions || v != e.Variables ? Expression.Block(e.Type, v, ex) : expr; } #endif case ExpressionType.Constant : return func(expr).Expression; case ExpressionType.Parameter: return func(expr).Expression; case (ExpressionType)ChangeTypeExpression.ChangeTypeType : { ci = func(expr); if (ci.Stop || ci.Expression != expr) return ci.Expression; var e = expr as ChangeTypeExpression; var ex = Convert2(e.Expression, func); if (ex == e.Expression) return expr; if (ex.Type == e.Type) return ex; return new ChangeTypeExpression(ex, e.Type); } } throw new InvalidOperationException(); } #endregion #region Helpers static public Expression Unwrap(this Expression ex) { if (ex == null) return null; switch (ex.NodeType) { case ExpressionType.Quote : return ((UnaryExpression)ex).Operand.Unwrap(); case ExpressionType.ConvertChecked : case ExpressionType.Convert : { var ue = (UnaryExpression)ex; if (!ue.Operand.Type.IsEnum) return ue.Operand.Unwrap(); break; } } return ex; } static public Dictionary<Expression,Expression> GetExpressionAccessors(this Expression expression, Expression path) { var accessors = new Dictionary<Expression,Expression>(); expression.Path(path, (e,p) => { switch (e.NodeType) { case ExpressionType.Call : case ExpressionType.MemberAccess : case ExpressionType.New : if (!accessors.ContainsKey(e)) accessors.Add(e, p); break; case ExpressionType.Constant : if (!accessors.ContainsKey(e)) accessors.Add(e, Expression.Property(p, ReflectionHelper.Constant.Value)); break; case ExpressionType.ConvertChecked : case ExpressionType.Convert : if (!accessors.ContainsKey(e)) { var ue = (UnaryExpression)e; switch (ue.Operand.NodeType) { case ExpressionType.Call : case ExpressionType.MemberAccess : case ExpressionType.New : case ExpressionType.Constant : accessors.Add(e, p); break; } } break; } }); return accessors; } static public Expression GetRootObject(this Expression expr) { if (expr == null) return null; switch (expr.NodeType) { case ExpressionType.Call : { var e = (MethodCallExpression)expr; if (e.Object != null) return GetRootObject(e.Object); if (e.Arguments != null && e.Arguments.Count > 0 && e.IsQueryable()) return GetRootObject(e.Arguments[0]); break; } case ExpressionType.MemberAccess : { var e = (MemberExpression)expr; if (e.Expression != null) return GetRootObject(e.Expression.Unwrap()); break; } } return expr; } static public List<Expression> GetMembers(this Expression expr) { if (expr == null) return new List<Expression>(); List<Expression> list; switch (expr.NodeType) { case ExpressionType.Call : { var e = (MethodCallExpression)expr; if (e.Object != null) list = GetMembers(e.Object); else if (e.Arguments != null && e.Arguments.Count > 0 && e.IsQueryable()) list = GetMembers(e.Arguments[0]); else list = new List<Expression>(); break; } case ExpressionType.MemberAccess : { var e = (MemberExpression)expr; if (e.Expression != null) list = GetMembers(e.Expression.Unwrap()); else list = new List<Expression>(); break; } default : list = new List<Expression>(); break; } list.Add(expr); return list; } static public bool IsQueryable(this MethodCallExpression method) { var type = method.Method.DeclaringType; return type == typeof(Queryable) || type == typeof(Enumerable) || type == typeof(LinqExtensions); } static public bool IsQueryable(this MethodCallExpression method, string name) { return method.Method.Name == name && method.IsQueryable(); } static public bool IsQueryable(this MethodCallExpression method, params string[] names) { if (method.IsQueryable()) foreach (var name in names) if (method.Method.Name == name) return true; return false; } static Expression FindLevel(Expression expression, int level, ref int current) { switch (expression.NodeType) { case ExpressionType.Call : { var call = (MethodCallExpression)expression; var expr = call.Object; if (expr == null && call.IsQueryable() && call.Arguments.Count > 0) expr = call.Arguments[0]; if (expr != null) { var ex = FindLevel(expr, level, ref current); if (level == current) return ex; current++; } break; } case ExpressionType.MemberAccess: { var e = ((MemberExpression)expression); if (e.Expression != null) { var expr = FindLevel(e.Expression.Unwrap(), level, ref current); if (level == current) return expr; current++; } break; } } return expression; } static public Expression GetLevelExpression(this Expression expression, int level) { var current = 0; var expr = FindLevel(expression, level, ref current); if (expr == null || current != level) throw new InvalidOperationException(); return expr; } #endregion } }