diff Source/Linq/ExpressionHelper.cs @ 0:f990fcb411a9

Копия текущей версии из github
author cin
date Thu, 27 Mar 2014 21:46:09 +0400
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Source/Linq/ExpressionHelper.cs	Thu Mar 27 21:46:09 2014 +0400
@@ -0,0 +1,2298 @@
+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
+	}
+}