diff Source/Data/Linq/Builder/ExpressionBuilder.SqlBuilder.cs @ 0:f990fcb411a9

Копия текущей версии из github
author cin
date Thu, 27 Mar 2014 21:46:09 +0400
parents
children 1ef98bd70424
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Source/Data/Linq/Builder/ExpressionBuilder.SqlBuilder.cs	Thu Mar 27 21:46:09 2014 +0400
@@ -0,0 +1,2509 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Text;
+
+namespace BLToolkit.Data.Linq.Builder
+{
+	using BLToolkit.Linq;
+	using Common;
+	using Data.Sql;
+	using Mapping;
+	using Reflection;
+
+	partial class ExpressionBuilder
+	{
+		#region Build Where
+
+		public IBuildContext BuildWhere(IBuildContext parent, IBuildContext sequence, LambdaExpression condition, bool checkForSubQuery)
+		{
+			var makeHaving = false;
+			var prevParent = sequence.Parent;
+
+			var ctx  = new ExpressionContext(parent, sequence, condition);
+			var expr = ConvertExpression(condition.Body.Unwrap());
+
+			if (checkForSubQuery && CheckSubQueryForWhere(ctx, expr, out makeHaving))
+			{
+				ReplaceParent(ctx, prevParent);
+
+				sequence = new SubQueryContext(sequence);
+				prevParent = sequence.Parent;
+
+				ctx = new ExpressionContext(parent, sequence, condition);
+			}
+
+			BuildSearchCondition(
+				ctx,
+				expr,
+				makeHaving ?
+					ctx.SqlQuery.Having.SearchCondition.Conditions :
+					ctx.SqlQuery.Where. SearchCondition.Conditions);
+
+			ReplaceParent(ctx, prevParent);
+
+			return sequence;
+		}
+
+		bool CheckSubQueryForWhere(IBuildContext context, Expression expression, out bool makeHaving)
+		{
+			var makeSubQuery = false;
+			var isHaving     = false;
+			var isWhere      = false;
+
+			expression.Visit(expr =>
+			{
+				if (_subQueryExpressions != null && _subQueryExpressions.Contains(expr))
+				{
+					makeSubQuery = true;
+					isWhere      = true;
+					return false;
+				}
+
+				var stopWalking = false;
+
+				switch (expr.NodeType)
+				{
+					case ExpressionType.MemberAccess:
+						{
+							var ma = (MemberExpression)expr;
+
+							if (TypeHelper.IsNullableValueMember(ma.Member) ||
+							    TypeHelper.IsNullableHasValueMember(ma.Member))
+								break;
+
+							if (SqlProvider.ConvertMember(ma.Member) == null)
+							{
+								var ctx = GetContext(context, expr);
+
+								if (ctx != null)
+								{
+									if (ctx.IsExpression(expr, 0, RequestFor.Expression).Result)
+										makeSubQuery = true;
+									stopWalking = true;
+								}
+							}
+
+							isWhere = true;
+
+							break;
+						}
+
+					case ExpressionType.Call:
+						{
+							var e = (MethodCallExpression)expr;
+
+							if (e.Method.DeclaringType == typeof(Enumerable) && e.Method.Name != "Contains")
+								return isHaving = true;
+
+							isWhere = true;
+
+							break;
+						}
+
+					case ExpressionType.Parameter:
+						{
+							var ctx = GetContext(context, expr);
+
+							if (ctx != null)
+							{
+								if (ctx.IsExpression(expr, 0, RequestFor.Expression).Result)
+									makeSubQuery = true;
+								stopWalking = true;
+							}
+
+							isWhere = true;
+
+							break;
+						}
+				}
+
+				return !stopWalking;
+			});
+
+			makeHaving = isHaving && !isWhere;
+			return makeSubQuery || isHaving && isWhere;
+		}
+
+		#endregion
+
+		#region BuildTake
+
+		public void BuildTake(IBuildContext context, ISqlExpression expr)
+		{
+			var sql = context.SqlQuery;
+
+			sql.Select.Take(expr);
+
+			SqlProvider.SqlQuery = sql;
+
+			if (sql.Select.SkipValue != null && SqlProvider.IsTakeSupported && !SqlProvider.IsSkipSupported)
+			{
+				if (context.SqlQuery.Select.SkipValue is SqlParameter && sql.Select.TakeValue is SqlValue)
+				{
+					var skip = (SqlParameter)sql.Select.SkipValue;
+					var parm = (SqlParameter)sql.Select.SkipValue.Clone(new Dictionary<ICloneableElement,ICloneableElement>(), _ => true);
+
+					parm.SetTakeConverter((int)((SqlValue)sql.Select.TakeValue).Value);
+
+					sql.Select.Take(parm);
+
+					var ep = (from pm in CurrentSqlParameters where pm.SqlParameter == skip select pm).First();
+
+					ep = new ParameterAccessor
+					{
+						Expression   = ep.Expression,
+						Accessor     = ep.Accessor,
+						SqlParameter = parm
+					};
+
+					CurrentSqlParameters.Add(ep);
+				}
+				else
+					sql.Select.Take(Convert(
+						context,
+						new SqlBinaryExpression(typeof(int), sql.Select.SkipValue, "+", sql.Select.TakeValue, Precedence.Additive)));
+			}
+
+			if (!SqlProvider.TakeAcceptsParameter)
+			{
+				var p = sql.Select.TakeValue as SqlParameter;
+
+				if (p != null)
+					p.IsQueryParameter = false;
+			}
+		}
+
+		#endregion
+
+		#region SubQueryToSql
+
+		public IBuildContext GetSubQuery(IBuildContext context, MethodCallExpression expr)
+		{
+			var info = new BuildInfo(context, expr, new SqlQuery { ParentSql = context.SqlQuery });
+			var ctx  = BuildSequence(info);
+
+			if (ctx.SqlQuery.Select.Columns.Count == 0 &&
+				(ctx.IsExpression(null, 0, RequestFor.Expression).Result ||
+				 ctx.IsExpression(null, 0, RequestFor.Field).     Result))
+			{
+				ctx.ConvertToIndex(null, 0, ConvertFlags.Field);
+			}
+
+			return ctx;
+		}
+
+		internal ISqlExpression SubQueryToSql(IBuildContext context, MethodCallExpression expression)
+		{
+			var sequence = GetSubQuery(context, expression);
+			var subSql   = sequence.GetSubQuery(context);
+
+			if (subSql != null)
+				return subSql;
+
+			var query    = context.SqlQuery;
+			var subQuery = sequence.SqlQuery;
+
+			// This code should be moved to context.
+			//
+			if (!query.GroupBy.IsEmpty && !subQuery.Where.IsEmpty)
+			{
+				var fromGroupBy = sequence.SqlQuery.Properties
+					.OfType<System.Tuple<string,SqlQuery>>()
+					.Where(p => p.Item1 == "from_group_by" && p.Item2 == context.SqlQuery)
+					.Any();
+
+				if (fromGroupBy)
+				{
+					if (subQuery.Select.Columns.Count == 1 &&
+					    subQuery.Select.Columns[0].Expression.ElementType == QueryElementType.SqlFunction &&
+					    subQuery.GroupBy.IsEmpty && !subQuery.Select.HasModifier && !subQuery.HasUnion &&
+					    subQuery.Where.SearchCondition.Conditions.Count == 1)
+					{
+						var cond = subQuery.Where.SearchCondition.Conditions[0];
+
+						if (cond.Predicate.ElementType == QueryElementType.ExprExprPredicate && query.GroupBy.Items.Count == 1 ||
+						    cond.Predicate.ElementType == QueryElementType.SearchCondition &&
+						    query.GroupBy.Items.Count == ((SqlQuery.SearchCondition)cond.Predicate).Conditions.Count)
+						{
+							var func = (SqlFunction)subQuery.Select.Columns[0].Expression;
+
+							if (CountBuilder.MethodNames.Contains(func.Name))
+								return SqlFunction.CreateCount(func.SystemType, query);
+						}
+					}
+				}
+			}
+
+			return sequence.SqlQuery;
+		}
+
+		#endregion
+
+		#region IsSubQuery
+
+		bool IsSubQuery(IBuildContext context, MethodCallExpression call)
+		{
+			if (call.IsQueryable())
+			{
+				var info = new BuildInfo(context, call, new SqlQuery { ParentSql = context.SqlQuery });
+
+				if (!IsSequence(info))
+					return false;
+
+				var arg = call.Arguments[0];
+
+				if (AggregationBuilder.MethodNames.Contains(call.Method.Name))
+					while (arg.NodeType == ExpressionType.Call && ((MethodCallExpression) arg).Method.Name == "Select")
+						arg = ((MethodCallExpression)arg).Arguments[0];
+
+				var mc = arg as MethodCallExpression;
+
+				while (mc != null)
+				{
+					if (!mc.IsQueryable())
+						return GetTableFunctionAttribute(mc.Method) != null;
+
+					mc = mc.Arguments[0] as MethodCallExpression;
+				}
+
+				return arg.NodeType == ExpressionType.Call || IsSubQuerySource(context, arg);
+			}
+
+			return false;
+		}
+
+		bool IsSubQuerySource(IBuildContext context, Expression expr)
+		{
+			if (expr == null)
+				return false;
+
+			var ctx = GetContext(context, expr);
+
+			if (ctx != null && ctx.IsExpression(expr, 0, RequestFor.Object).Result)
+				return true;
+
+			while (expr != null && expr.NodeType == ExpressionType.MemberAccess)
+				expr = ((MemberExpression)expr).Expression;
+
+			return expr != null && expr.NodeType == ExpressionType.Constant;
+		}
+
+		bool IsGroupJoinSource(IBuildContext context, MethodCallExpression call)
+		{
+			if (!call.IsQueryable() || CountBuilder.MethodNames.Contains(call.Method.Name))
+				return false;
+
+			Expression expr = call;
+
+			while (expr.NodeType == ExpressionType.Call)
+				expr = ((MethodCallExpression)expr).Arguments[0];
+
+			var ctx = GetContext(context, expr);
+
+			return ctx != null && ctx.IsExpression(expr, 0, RequestFor.GroupJoin).Result;
+		}
+
+		#endregion
+
+		#region ConvertExpression
+
+		interface IConvertHelper
+		{
+			Expression ConvertNull(MemberExpression expression);
+		}
+
+		class ConvertHelper<T> : IConvertHelper
+			where T : struct 
+		{
+			public Expression ConvertNull(MemberExpression expression)
+			{
+				return Expression.Call(
+					null,
+					ReflectionHelper.Expressor<T?>.MethodExpressor(p => Sql.ConvertNullable(p)),
+					expression.Expression);
+			}
+		}
+
+		Expression ConvertExpression(Expression expression)
+		{
+			return expression.Convert2(e =>
+			{
+				if (CanBeConstant(e) || CanBeCompiled(e))
+					return new ExpressionHelper.ConvertInfo(e, true);
+
+				switch (e.NodeType)
+				{
+					case ExpressionType.New:
+						{
+							var ex = ConvertNew((NewExpression)e);
+							if (ex != null)
+								return new ExpressionHelper.ConvertInfo(ConvertExpression(ex));
+							break;
+						}
+
+					case ExpressionType.Call:
+						{
+							var cm = ConvertMethod((MethodCallExpression)e);
+							if (cm != null)
+								return new ExpressionHelper.ConvertInfo(ConvertExpression(cm));
+							break;
+						}
+
+					case ExpressionType.MemberAccess:
+						{
+							var ma = (MemberExpression)e;
+							var l  = SqlProvider.ConvertMember(ma.Member);
+
+							if (l != null)
+							{
+								var body = l.Body.Unwrap();
+								var expr = body.Convert(wpi => wpi.NodeType == ExpressionType.Parameter ? ma.Expression : wpi);
+
+								if (expr.Type != e.Type)
+									expr = new ChangeTypeExpression(expr, e.Type);
+
+								return new ExpressionHelper.ConvertInfo(ConvertExpression(expr));
+							}
+
+							if (TypeHelper.IsNullableValueMember(ma.Member))
+							{
+								var ntype  = typeof(ConvertHelper<>).MakeGenericType(ma.Type);
+								var helper = (IConvertHelper)Activator.CreateInstance(ntype);
+								var expr   = helper.ConvertNull(ma);
+
+								return new ExpressionHelper.ConvertInfo(ConvertExpression(expr));
+							}
+
+							if (ma.Member.DeclaringType == typeof(TimeSpan))
+							{
+								switch (ma.Expression.NodeType)
+								{
+									case ExpressionType.Subtract       :
+									case ExpressionType.SubtractChecked:
+
+										Sql.DateParts datePart;
+
+										switch (ma.Member.Name)
+										{
+											case "TotalMilliseconds" : datePart = Sql.DateParts.Millisecond; break;
+											case "TotalSeconds"      : datePart = Sql.DateParts.Second;      break;
+											case "TotalMinutes"      : datePart = Sql.DateParts.Minute;      break;
+											case "TotalHours"        : datePart = Sql.DateParts.Hour;        break;
+											case "TotalDays"         : datePart = Sql.DateParts.Day;         break;
+											default                  : return new ExpressionHelper.ConvertInfo(e);
+										}
+
+										var ex     = (BinaryExpression)ma.Expression;
+										var method = ReflectionHelper.Expressor<object>.MethodExpressor(
+											_ => Sql.DateDiff(Sql.DateParts.Day, DateTime.MinValue, DateTime.MinValue));
+
+										var call   =
+											Expression.Convert(
+												Expression.Call(
+													null,
+													method,
+													Expression.Constant(datePart),
+													Expression.Convert(ex.Right, typeof(DateTime?)),
+													Expression.Convert(ex.Left,  typeof(DateTime?))),
+												typeof(double));
+
+										return new ExpressionHelper.ConvertInfo(ConvertExpression(call));
+								}
+							}
+
+							break;
+						}
+				}
+
+				return new ExpressionHelper.ConvertInfo(e);
+			});
+		}
+
+		Expression ConvertMethod(MethodCallExpression pi)
+		{
+			var l = SqlProvider.ConvertMember(pi.Method);
+			return l == null ? null : ConvertMethod(pi, l);
+		}
+
+		static Expression ConvertMethod(MethodCallExpression pi, LambdaExpression lambda)
+		{
+			var ef    = lambda.Body.Unwrap();
+			var parms = new Dictionary<string,int>(lambda.Parameters.Count);
+			var pn    = pi.Method.IsStatic ? 0 : -1;
+
+			foreach (var p in lambda.Parameters)
+				parms.Add(p.Name, pn++);
+
+			var pie = ef.Convert(wpi =>
+			{
+				if (wpi.NodeType == ExpressionType.Parameter)
+				{
+					int n;
+					if (parms.TryGetValue(((ParameterExpression)wpi).Name, out n))
+						return n < 0 ? pi.Object : pi.Arguments[n];
+				}
+
+				return wpi;
+			});
+
+			if (pi.Method.ReturnType != pie.Type)
+				pie = new ChangeTypeExpression(pie, pi.Method.ReturnType);
+
+			return pie;
+		}
+
+		Expression ConvertNew(NewExpression pi)
+		{
+			var lambda = SqlProvider.ConvertMember(pi.Constructor);
+
+			if (lambda != null)
+			{
+				var ef    = lambda.Body.Unwrap();
+				var parms = new Dictionary<string,int>(lambda.Parameters.Count);
+				var pn    = 0;
+
+				foreach (var p in lambda.Parameters)
+					parms.Add(p.Name, pn++);
+
+				return ef.Convert(wpi =>
+				{
+					if (wpi.NodeType == ExpressionType.Parameter)
+					{
+						var pe   = (ParameterExpression)wpi;
+						var n    = parms[pe.Name];
+						return pi.Arguments[n];
+					}
+
+					return wpi;
+				});
+			}
+
+			return null;
+		}
+
+		#endregion
+
+		#region BuildExpression
+
+		public SqlInfo[] ConvertExpressions(IBuildContext context, Expression expression, ConvertFlags queryConvertFlag)
+		{
+			expression = ConvertExpression(expression);
+
+			switch (expression.NodeType)
+			{
+				case ExpressionType.New :
+					{
+						var expr = (NewExpression)expression;
+
+// ReSharper disable ConditionIsAlwaysTrueOrFalse
+// ReSharper disable HeuristicUnreachableCode
+						if (expr.Members == null)
+							return Array<SqlInfo>.Empty;
+// ReSharper restore HeuristicUnreachableCode
+// ReSharper restore ConditionIsAlwaysTrueOrFalse
+
+						return expr.Arguments
+							.Select((arg,i) =>
+							{
+								var mi = expr.Members[i];
+								if (mi is MethodInfo)
+									mi = TypeHelper.GetPropertyByMethod((MethodInfo)mi);
+
+								return ConvertExpressions(context, arg, queryConvertFlag).Select(si => si.Clone(mi));
+							})
+							.SelectMany(si => si)
+							.ToArray();
+					}
+
+				case ExpressionType.MemberInit :
+					{
+						var expr = (MemberInitExpression)expression;
+						var dic  = TypeAccessor.GetAccessor(expr.Type)
+							.Select((m,i) => new { m, i })
+							.ToDictionary(_ => _.m.MemberInfo, _ => _.i);
+
+						return expr.Bindings
+							.Where  (b => b is MemberAssignment)
+							.Cast<MemberAssignment>()
+							.OrderBy(b => dic[b.Member])
+							.Select (a =>
+							{
+								var mi = a.Member;
+								if (mi is MethodInfo)
+									mi = TypeHelper.GetPropertyByMethod((MethodInfo)mi);
+
+								return ConvertExpressions(context, a.Expression, queryConvertFlag).Select(si => si.Clone(mi));
+							})
+							.SelectMany(si => si)
+							.ToArray();
+					}
+			}
+
+			var ctx = GetContext(context, expression);
+
+			if (ctx != null && ctx.IsExpression(expression, 0, RequestFor.Object).Result)
+				return ctx.ConvertToSql(expression, 0, queryConvertFlag);
+
+			return new[] { new SqlInfo { Sql = ConvertToSql(context, expression, false) } };
+		}
+
+		public ISqlExpression ConvertToSqlExpression(IBuildContext context, Expression expression, bool convertEnum)
+		{
+			var expr = ConvertExpression(expression);
+			return ConvertToSql(context, expr, false, convertEnum);
+		}
+
+#if FW3
+		public ISqlExpression ConvertToSql(IBuildContext context, Expression expression, bool unwrap)
+		{
+			return ConvertToSql(context, expression, unwrap, true);
+		}
+#endif
+
+		public ISqlExpression ConvertToSql(IBuildContext context, Expression expression, bool unwrap, bool convertEnum
+#if !FW3
+			= true
+#endif
+			)
+		{
+			if (CanBeConstant(expression))
+				return BuildConstant(expression, convertEnum);
+
+			if (CanBeCompiled(expression))
+				return BuildParameter(expression).SqlParameter;
+
+			if (unwrap)
+				expression = expression.Unwrap();
+
+			switch (expression.NodeType)
+			{
+				case ExpressionType.AndAlso            :
+				case ExpressionType.OrElse             :
+				case ExpressionType.Not                :
+				case ExpressionType.Equal              :
+				case ExpressionType.NotEqual           :
+				case ExpressionType.GreaterThan        :
+				case ExpressionType.GreaterThanOrEqual :
+				case ExpressionType.LessThan           :
+				case ExpressionType.LessThanOrEqual    :
+					{
+						var condition = new SqlQuery.SearchCondition();
+						BuildSearchCondition(context, expression, condition.Conditions);
+						return condition;
+					}
+
+				case ExpressionType.And                :
+				case ExpressionType.Or                 :
+					{
+						if (expression.Type == typeof(bool))
+							goto case ExpressionType.AndAlso;
+						goto case ExpressionType.Add;
+					}
+
+				case ExpressionType.Add                :
+				case ExpressionType.AddChecked         :
+				case ExpressionType.Divide             :
+				case ExpressionType.ExclusiveOr        :
+				case ExpressionType.Modulo             :
+				case ExpressionType.Multiply           :
+				case ExpressionType.MultiplyChecked    :
+				case ExpressionType.Power              :
+				case ExpressionType.Subtract           :
+				case ExpressionType.SubtractChecked    :
+				case ExpressionType.Coalesce           :
+					{
+						var e = (BinaryExpression)expression;
+						var l = ConvertToSql(context, e.Left,  false);
+						var r = ConvertToSql(context, e.Right, false);
+						var t = e.Type;
+
+						switch (expression.NodeType)
+						{
+							case ExpressionType.Add             :
+							case ExpressionType.AddChecked      : return Convert(context, new SqlBinaryExpression(t, l, "+", r, Precedence.Additive));
+							case ExpressionType.And             : return Convert(context, new SqlBinaryExpression(t, l, "&", r, Precedence.Bitwise));
+							case ExpressionType.Divide          : return Convert(context, new SqlBinaryExpression(t, l, "/", r, Precedence.Multiplicative));
+							case ExpressionType.ExclusiveOr     : return Convert(context, new SqlBinaryExpression(t, l, "^", r, Precedence.Bitwise));
+							case ExpressionType.Modulo          : return Convert(context, new SqlBinaryExpression(t, l, "%", r, Precedence.Multiplicative));
+							case ExpressionType.Multiply:
+							case ExpressionType.MultiplyChecked : return Convert(context, new SqlBinaryExpression(t, l, "*", r, Precedence.Multiplicative));
+							case ExpressionType.Or              : return Convert(context, new SqlBinaryExpression(t, l, "|", r, Precedence.Bitwise));
+							case ExpressionType.Power           : return Convert(context, new SqlFunction(t, "Power", l, r));
+							case ExpressionType.Subtract        :
+							case ExpressionType.SubtractChecked : return Convert(context, new SqlBinaryExpression(t, l, "-", r, Precedence.Subtraction));
+							case ExpressionType.Coalesce        :
+								{
+									if (r is SqlFunction)
+									{
+										var c = (SqlFunction)r;
+
+										if (c.Name == "Coalesce")
+										{
+											var parms = new ISqlExpression[c.Parameters.Length + 1];
+
+											parms[0] = l;
+											c.Parameters.CopyTo(parms, 1);
+
+											return Convert(context, new SqlFunction(t, "Coalesce", parms));
+										}
+									}
+
+									return Convert(context, new SqlFunction(t, "Coalesce", l, r));
+								}
+						}
+
+						break;
+					}
+
+				case ExpressionType.UnaryPlus      :
+				case ExpressionType.Negate         :
+				case ExpressionType.NegateChecked  :
+					{
+						var e = (UnaryExpression)expression;
+						var o = ConvertToSql(context, e.Operand, false);
+						var t = e.Type;
+
+						switch (expression.NodeType)
+						{
+							case ExpressionType.UnaryPlus     : return o;
+							case ExpressionType.Negate        :
+							case ExpressionType.NegateChecked :
+								return Convert(context, new SqlBinaryExpression(t, new SqlValue(-1), "*", o, Precedence.Multiplicative));
+						}
+
+						break;
+					}
+
+				case ExpressionType.Convert        :
+				case ExpressionType.ConvertChecked :
+					{
+						var e = (UnaryExpression)expression;
+						var o = ConvertToSql(context, e.Operand, false);
+
+						if (e.Method == null && e.IsLifted)
+							return o;
+
+						var t = e.Operand.Type;
+						var s = SqlDataType.GetDataType(t);
+
+						if (o.SystemType != null && s.Type == typeof(object))
+						{
+							t = o.SystemType;
+							s = SqlDataType.GetDataType(t);
+						}
+
+						if (e.Type == t ||
+							t.IsEnum && Enum.GetUnderlyingType(t) == e.Type ||
+							e.Type.IsEnum && Enum.GetUnderlyingType(e.Type) == t)
+							return o;
+
+						return Convert(
+							context,
+							new SqlFunction(e.Type, "$Convert$", SqlDataType.GetDataType(e.Type), s, o));
+					}
+
+				case ExpressionType.Conditional    :
+					{
+						var e = (ConditionalExpression)expression;
+						var s = ConvertToSql(context, e.Test,    false);
+						var t = ConvertToSql(context, e.IfTrue,  false);
+						var f = ConvertToSql(context, e.IfFalse, false);
+
+						if (f is SqlFunction)
+						{
+							var c = (SqlFunction)f;
+
+							if (c.Name == "CASE")
+							{
+								var parms = new ISqlExpression[c.Parameters.Length + 2];
+
+								parms[0] = s;
+								parms[1] = t;
+								c.Parameters.CopyTo(parms, 2);
+
+								return Convert(context, new SqlFunction(e.Type, "CASE", parms));
+							}
+						}
+
+						return Convert(context, new SqlFunction(e.Type, "CASE", s, t, f));
+					}
+
+				case ExpressionType.MemberAccess :
+					{
+						var ma   = (MemberExpression)expression;
+						var attr = GetFunctionAttribute(ma.Member);
+
+						if (attr != null)
+							return Convert(context, attr.GetExpression(ma.Member));
+
+						var ctx = GetContext(context, expression);
+
+						if (ctx != null)
+						{
+							var sql = ctx.ConvertToSql(expression, 0, ConvertFlags.Field);
+
+							switch (sql.Length)
+							{
+								case 0  : break;
+								case 1  : return sql[0].Sql;
+								default : throw new InvalidOperationException();
+							}
+						}
+
+						break;
+					}
+
+				case ExpressionType.Parameter   :
+					{
+						var ctx = GetContext(context, expression);
+
+						if (ctx != null)
+						{
+							var sql = ctx.ConvertToSql(expression, 0, ConvertFlags.Field);
+
+							switch (sql.Length)
+							{
+								case 0  : break;
+								case 1  : return sql[0].Sql;
+								default : throw new InvalidOperationException();
+							}
+						}
+
+						break;
+					}
+
+				case ExpressionType.Call        :
+					{
+						var e = (MethodCallExpression)expression;
+
+						if (e.IsQueryable())
+						{
+							if (IsSubQuery(context, e))
+								return SubQueryToSql(context, e);
+
+							if (CountBuilder.MethodNames.Concat(AggregationBuilder.MethodNames).Contains(e.Method.Name))
+							{
+								var ctx = GetContext(context, expression);
+
+								if (ctx != null)
+								{
+									var sql = ctx.ConvertToSql(expression, 0, ConvertFlags.Field);
+
+									if (sql.Length != 1)
+										throw new InvalidOperationException();
+
+									return sql[0].Sql;
+								}
+
+								break;
+							}
+
+							return SubQueryToSql(context, e);
+						}
+
+						var attr = GetFunctionAttribute(e.Method);
+
+						if (attr != null)
+						{
+							var parms = new List<ISqlExpression>();
+
+							if (e.Object != null)
+								parms.Add(ConvertToSql(context, e.Object, false));
+
+							parms.AddRange(e.Arguments.Select(t => ConvertToSql(context, t, false)));
+
+							return Convert(context, attr.GetExpression(e.Method, parms.ToArray()));
+						}
+
+						break;
+					}
+
+				case ExpressionType.Invoke :
+					{
+						var pi = (InvocationExpression)expression;
+						var ex = pi.Expression;
+
+						if (ex.NodeType == ExpressionType.Quote)
+							ex = ((UnaryExpression)ex).Operand;
+
+						if (ex.NodeType == ExpressionType.Lambda)
+						{
+							var l   = (LambdaExpression)ex;
+							var dic = new Dictionary<Expression,Expression>();
+
+							for (var i = 0; i < l.Parameters.Count; i++)
+								dic.Add(l.Parameters[i], pi.Arguments[i]);
+
+							var pie = l.Body.Convert(wpi =>
+							{
+								Expression ppi;
+								return dic.TryGetValue(wpi, out ppi) ? ppi : wpi;
+							});
+
+							return ConvertToSql(context, pie, false);
+						}
+
+						break;
+					}
+
+				case ExpressionType.TypeIs :
+					{
+						var condition = new SqlQuery.SearchCondition();
+						BuildSearchCondition(context, expression, condition.Conditions);
+						return condition;
+					}
+
+				case (ExpressionType)ChangeTypeExpression.ChangeTypeType :
+					return ConvertToSql(context, ((ChangeTypeExpression)expression).Expression, false);
+			}
+
+			if (expression.Type == typeof(bool) && _convertedPredicates.Add(expression))
+			{
+				var predicate = ConvertPredicate(context, expression);
+				_convertedPredicates.Remove(expression);
+				if (predicate != null)
+					return new SqlQuery.SearchCondition(new SqlQuery.Condition(false, predicate));
+			}
+
+			throw new LinqException("'{0}' cannot be converted to SQL.", expression);
+		}
+
+		readonly HashSet<Expression> _convertedPredicates = new HashSet<Expression>();
+
+		#endregion
+
+		#region IsServerSideOnly
+
+		bool IsServerSideOnly(Expression expr)
+		{
+			switch (expr.NodeType)
+			{
+				case ExpressionType.MemberAccess:
+					{
+						var ex = (MemberExpression)expr;
+						var l  = SqlProvider.ConvertMember(ex.Member);
+
+						if (l != null)
+							return IsServerSideOnly(l.Body.Unwrap());
+
+						var attr = GetFunctionAttribute(ex.Member);
+						return attr != null && attr.ServerSideOnly;
+					}
+
+				case ExpressionType.Call:
+					{
+						var e = (MethodCallExpression)expr;
+
+						if (e.Method.DeclaringType == typeof(Enumerable))
+						{
+							if (CountBuilder.MethodNames.Concat(AggregationBuilder.MethodNames).Contains(e.Method.Name))
+								return IsQueryMember(e.Arguments[0]);
+						}
+						else if (e.Method.DeclaringType == typeof(Queryable))
+						{
+							switch (e.Method.Name)
+							{
+								case "Any"      :
+								case "All"      :
+								case "Contains" : return true;
+							}
+						}
+						else
+						{
+							var l = SqlProvider.ConvertMember(e.Method);
+
+							if (l != null)
+								return l.Body.Unwrap().Find(IsServerSideOnly) != null;
+
+							var attr = GetFunctionAttribute(e.Method);
+							return attr != null && attr.ServerSideOnly;
+						}
+
+						break;
+					}
+			}
+
+			return false;
+		}
+
+		static bool IsQueryMember(Expression expr)
+		{
+			if (expr != null) switch (expr.NodeType)
+			{
+				case ExpressionType.Parameter    : return true;
+				case ExpressionType.MemberAccess : return IsQueryMember(((MemberExpression)expr).Expression);
+				case ExpressionType.Call         :
+					{
+						var call = (MethodCallExpression)expr;
+
+						if (call.Method.DeclaringType == typeof(Queryable))
+							return true;
+
+						if (call.Method.DeclaringType == typeof(Enumerable) && call.Arguments.Count > 0)
+							return IsQueryMember(call.Arguments[0]);
+
+						return IsQueryMember(call.Object);
+					}
+			}
+
+			return false;
+		}
+
+		#endregion
+
+		#region CanBeConstant
+
+		bool CanBeConstant(Expression expr)
+		{
+			return null == expr.Find(ex =>
+			{
+				if (ex is BinaryExpression || ex is UnaryExpression /*|| ex.NodeType == ExpressionType.Convert*/)
+					return false;
+
+				switch (ex.NodeType)
+				{
+					case ExpressionType.Constant     :
+						{
+							var c = (ConstantExpression)ex;
+
+							if (c.Value == null || ExpressionHelper.IsConstant(ex.Type))
+								return false;
+
+							break;
+						}
+
+					case ExpressionType.MemberAccess :
+						{
+							var ma = (MemberExpression)ex;
+
+							if (ExpressionHelper.IsConstant(ma.Member.DeclaringType) || TypeHelper.IsNullableValueMember(ma.Member))
+								return false;
+
+							break;
+						}
+
+					case ExpressionType.Call         :
+						{
+							var mc = (MethodCallExpression)ex;
+
+							if (ExpressionHelper.IsConstant(mc.Method.DeclaringType) || mc.Method.DeclaringType == typeof(object))
+								return false;
+
+							var attr = GetFunctionAttribute(mc.Method);
+
+							if (attr != null && !attr.ServerSideOnly)
+								return false;
+
+							break;
+						}
+				}
+
+				return true;
+			});
+		}
+
+		#endregion
+
+		#region CanBeCompiled
+
+		bool CanBeCompiled(Expression expr)
+		{
+			return null == expr.Find(ex =>
+			{
+				if (IsServerSideOnly(ex))
+					return true;
+
+				switch (ex.NodeType)
+				{
+					case ExpressionType.Parameter    :
+						return ex != ParametersParam;
+
+					case ExpressionType.MemberAccess :
+						{
+							var attr = GetFunctionAttribute(((MemberExpression)ex).Member);
+							return attr != null && attr.ServerSideOnly;
+						}
+
+					case ExpressionType.Call         :
+						{
+							var attr = GetFunctionAttribute(((MethodCallExpression)ex).Method);
+							return attr != null && attr.ServerSideOnly;
+						}
+				}
+
+				return false;
+			});
+		}
+
+		#endregion
+
+		#region Build Constant
+
+		readonly Dictionary<Expression,SqlValue> _constants = new Dictionary<Expression,SqlValue>();
+
+		SqlValue BuildConstant(Expression expr, bool convertEnum)
+		{
+			SqlValue value;
+
+			if (_constants.TryGetValue(expr, out value))
+				return value;
+
+			var lambda = Expression.Lambda<Func<object>>(Expression.Convert(expr, typeof(object)));
+			var v      = lambda.Compile()();
+
+			if (v != null && convertEnum && v.GetType().IsEnum)
+			{
+				var attrs = v.GetType().GetCustomAttributes(typeof(SqlEnumAttribute), true);
+
+				v = Map.EnumToValue(v, attrs.Length == 0);
+			}
+
+			value = new SqlValue(v);
+
+			_constants.Add(expr, value);
+
+			return value;
+		}
+
+		#endregion
+
+		#region Build Parameter
+
+		readonly Dictionary<Expression,ParameterAccessor> _parameters = new Dictionary<Expression, ParameterAccessor>();
+
+		public readonly HashSet<Expression> AsParameters = new HashSet<Expression>();
+
+		ParameterAccessor BuildParameter(Expression expr)
+		{
+			ParameterAccessor p;
+
+			if (_parameters.TryGetValue(expr, out p))
+				return p;
+
+			string name = null;
+
+			var newExpr = ReplaceParameter(_expressionAccessors, expr, nm => name = nm);
+			var mapper  = Expression.Lambda<Func<Expression,object[],object>>(
+				Expression.Convert(newExpr, typeof(object)),
+				new [] { ExpressionParam, ParametersParam });
+
+			p = new ParameterAccessor
+			{
+				Expression   = expr,
+				Accessor     = mapper.Compile(),
+				SqlParameter = new SqlParameter(expr.Type, name, null, MappingSchema)
+			};
+
+			_parameters.Add(expr, p);
+			CurrentSqlParameters.Add(p);
+
+			return p;
+		}
+
+		Expression ReplaceParameter(IDictionary<Expression,Expression> expressionAccessors, Expression expression, Action<string> setName)
+		{
+			return expression.Convert(expr =>
+			{
+				if (expr.NodeType == ExpressionType.Constant)
+				{
+					var c = (ConstantExpression)expr;
+
+					if (!ExpressionHelper.IsConstant(expr.Type) || AsParameters.Contains(c))
+					{
+						Expression val;
+						
+						if (expressionAccessors.TryGetValue(expr, out val))
+						{
+							expr = Expression.Convert(val, expr.Type);
+
+							if (expression.NodeType == ExpressionType.MemberAccess)
+							{
+								var ma = (MemberExpression)expression;
+								setName(ma.Member.Name);
+							}
+						}
+					}
+				}
+
+				return expr;
+			});
+		}
+
+		#endregion
+
+		#region Predicate Converter
+
+		ISqlPredicate ConvertPredicate(IBuildContext context, Expression expression)
+		{
+			switch (expression.NodeType)
+			{
+				case ExpressionType.Equal              :
+				case ExpressionType.NotEqual           :
+				case ExpressionType.GreaterThan        :
+				case ExpressionType.GreaterThanOrEqual :
+				case ExpressionType.LessThan           :
+				case ExpressionType.LessThanOrEqual    :
+					{
+						var e = (BinaryExpression)expression;
+						return ConvertCompare(context, expression.NodeType, e.Left, e.Right);
+					}
+
+				case ExpressionType.Call               :
+					{
+						var e = (MethodCallExpression)expression;
+
+						ISqlPredicate predicate = null;
+
+						if (e.Method.Name == "Equals" && e.Object != null && e.Arguments.Count == 1)
+							return ConvertCompare(context, ExpressionType.Equal, e.Object, e.Arguments[0]);
+
+						if (e.Method.DeclaringType == typeof(string))
+						{
+							switch (e.Method.Name)
+							{
+								case "Contains"   : predicate = ConvertLikePredicate(context, e, "%", "%"); break;
+								case "StartsWith" : predicate = ConvertLikePredicate(context, e, "",  "%"); break;
+								case "EndsWith"   : predicate = ConvertLikePredicate(context, e, "%", "");  break;
+							}
+						}
+						else if (e.Method.Name == "Contains")
+						{
+							if (e.Method.DeclaringType == typeof(Enumerable) ||
+							    TypeHelper.IsSameOrParent(typeof(IList), e.Method.DeclaringType) ||
+							    TypeHelper.IsSameOrParent(typeof(ICollection<>), e.Method.DeclaringType))
+							{
+								predicate = ConvertInPredicate(context, e);
+							}
+						}
+						else if (e.Method.Name == "ContainsValue" && TypeHelper.IsSameOrParent(typeof(Dictionary<,>), e.Method.DeclaringType))
+						{
+							var args = TypeHelper.GetGenericArguments(e.Method.DeclaringType, typeof(Dictionary<,>));
+							var minf = EnumerableMethods
+								.First(m => m.Name == "Contains" && m.GetParameters().Length == 2)
+								.MakeGenericMethod(args[1]);
+
+							var expr = Expression.Call(
+								minf, 
+								Expression.PropertyOrField(e.Object, "Values"),
+								e.Arguments[0]);
+
+							predicate = ConvertInPredicate(context, expr);
+						}
+						else if (e.Method.Name == "ContainsKey" && TypeHelper.IsSameOrParent(typeof(IDictionary<,>), e.Method.DeclaringType))
+						{
+							var args = TypeHelper.GetGenericArguments(e.Method.DeclaringType, typeof(IDictionary<,>));
+							var minf = EnumerableMethods
+								.First(m => m.Name == "Contains" && m.GetParameters().Length == 2)
+								.MakeGenericMethod(args[0]);
+
+							var expr = Expression.Call(
+								minf, 
+								Expression.PropertyOrField(e.Object, "Keys"),
+								e.Arguments[0]);
+
+							predicate = ConvertInPredicate(context, expr);
+						}
+#if !SILVERLIGHT
+						else if (e.Method == ReflectionHelper.Functions.String.Like11) predicate = ConvertLikePredicate(context, e);
+						else if (e.Method == ReflectionHelper.Functions.String.Like12) predicate = ConvertLikePredicate(context, e);
+#endif
+						else if (e.Method == ReflectionHelper.Functions.String.Like21) predicate = ConvertLikePredicate(context, e);
+						else if (e.Method == ReflectionHelper.Functions.String.Like22) predicate = ConvertLikePredicate(context, e);
+
+						if (predicate != null)
+							return Convert(context, predicate);
+
+						break;
+					}
+
+				case ExpressionType.Conditional  :
+					return Convert(context,
+						new SqlQuery.Predicate.ExprExpr(
+							ConvertToSql(context, expression, false),
+							SqlQuery.Predicate.Operator.Equal,
+							new SqlValue(true)));
+
+				case ExpressionType.MemberAccess :
+					{
+						var e = (MemberExpression)expression;
+
+						if (e.Member.Name == "HasValue" && 
+							e.Member.DeclaringType.IsGenericType && 
+							e.Member.DeclaringType.GetGenericTypeDefinition() == typeof(Nullable<>))
+						{
+							var expr = ConvertToSql(context, e.Expression, false);
+							return Convert(context, new SqlQuery.Predicate.IsNull(expr, true));
+						}
+
+						break;
+					}
+
+				case ExpressionType.TypeIs:
+					{
+						var e   = (TypeBinaryExpression)expression;
+						var ctx = GetContext(context, e.Expression);
+
+						if (ctx != null && ctx.IsExpression(e.Expression, 0, RequestFor.Table).Result)
+							return MakeIsPredicate(ctx, e);
+
+						break;
+					}
+			}
+
+			var ex = ConvertToSql(context, expression, false);
+
+			if (SqlExpression.NeedsEqual(ex))
+				return Convert(context, new SqlQuery.Predicate.ExprExpr(ex, SqlQuery.Predicate.Operator.Equal, new SqlValue(true)));
+
+			return Convert(context, new SqlQuery.Predicate.Expr(ex));
+		}
+
+		#region ConvertCompare
+
+		ISqlPredicate ConvertCompare(IBuildContext context, ExpressionType nodeType, Expression left, Expression right)
+		{
+			if (left.NodeType == ExpressionType.Convert && left.Type == typeof(int) && right.NodeType == ExpressionType.Constant)
+			{
+				var conv = (UnaryExpression)left;
+
+				if (conv.Operand.Type == typeof(char))
+				{
+					left  = conv.Operand;
+					right = Expression.Constant(ConvertTo<char>.From(((ConstantExpression)right).Value));
+				}
+			}
+
+			if (right.NodeType == ExpressionType.Convert && right.Type == typeof(int) && left.NodeType == ExpressionType.Constant)
+			{
+				var conv = (UnaryExpression)right;
+
+				if (conv.Operand.Type == typeof(char))
+				{
+					right = conv.Operand;
+					left  = Expression.Constant(ConvertTo<char>.From(((ConstantExpression)left).Value));
+				}
+			}
+
+			#region special case for char?
+
+//			if (left.NodeType == ExpressionType.Convert && left.Type == typeof(int?) && right.NodeType == ExpressionType.Convert)
+//			{
+//				var convLeft  = left  as UnaryExpression;
+//				var convRight = right as UnaryExpression;
+//
+//				if (convLeft != null && convRight != null && convLeft.Operand.Type == typeof(char?))
+//				{
+//					left  = convLeft.Operand;
+//					right = Expression.Constant(ConvertTo<char?>.From(((ConstantExpression)convRight.Operand).Value));
+//				}
+//			}
+
+			#endregion
+
+			switch (nodeType)
+			{
+				case ExpressionType.Equal    :
+				case ExpressionType.NotEqual :
+
+					var p = ConvertObjectComparison(nodeType, context, left, context, right);
+					if (p != null)
+						return p;
+
+					p = ConvertObjectNullComparison(context, left, right, nodeType == ExpressionType.Equal);
+					if (p != null)
+						return p;
+
+					p = ConvertObjectNullComparison(context, right, left, nodeType == ExpressionType.Equal);
+					if (p != null)
+						return p;
+
+					if (left.NodeType == ExpressionType.New || right.NodeType == ExpressionType.New)
+					{
+						p = ConvertNewObjectComparison(context, nodeType, left, right);
+						if (p != null)
+							return p;
+					}
+
+					break;
+			}
+
+			SqlQuery.Predicate.Operator op;
+
+			switch (nodeType)
+			{
+				case ExpressionType.Equal             : op = SqlQuery.Predicate.Operator.Equal;          break;
+				case ExpressionType.NotEqual          : op = SqlQuery.Predicate.Operator.NotEqual;       break;
+				case ExpressionType.GreaterThan       : op = SqlQuery.Predicate.Operator.Greater;        break;
+				case ExpressionType.GreaterThanOrEqual: op = SqlQuery.Predicate.Operator.GreaterOrEqual; break;
+				case ExpressionType.LessThan          : op = SqlQuery.Predicate.Operator.Less;           break;
+				case ExpressionType.LessThanOrEqual   : op = SqlQuery.Predicate.Operator.LessOrEqual;    break;
+				default: throw new InvalidOperationException();
+			}
+
+			if (left.NodeType == ExpressionType.Convert || right.NodeType == ExpressionType.Convert
+				|| left.NodeType == ExpressionType.MemberAccess || right.NodeType == ExpressionType.MemberAccess)
+			{
+				var p = ConvertEnumConversion(context, left, op, right);
+				if (p != null)
+					return p;
+			}
+
+			var l = ConvertToSql(context, left,  false);
+			var r = ConvertToSql(context, right, true);
+
+			switch (nodeType)
+			{
+				case ExpressionType.Equal   :
+				case ExpressionType.NotEqual:
+
+					if (!context.SqlQuery.IsParameterDependent && (l is SqlParameter || r is SqlParameter) && l.CanBeNull() && r.CanBeNull())
+						context.SqlQuery.IsParameterDependent = true;
+
+					// | (SqlQuery(Select([]) as q), SqlValue(null))
+					// | (SqlValue(null), SqlQuery(Select([]) as q))  =>
+
+					SqlQuery q =
+						l.ElementType == QueryElementType.SqlQuery &&
+						r.ElementType == QueryElementType.SqlValue &&
+						((SqlValue)r).Value == null &&
+						((SqlQuery)l).Select.Columns.Count == 0 ?
+							(SqlQuery)l :
+						r.ElementType == QueryElementType.SqlQuery &&
+						l.ElementType == QueryElementType.SqlValue &&
+						((SqlValue)l).Value == null &&
+						((SqlQuery)r).Select.Columns.Count == 0 ?
+							(SqlQuery)r :
+							null;
+
+					if (q != null)
+					{
+						q.Select.Columns.Add(new SqlQuery.Column(q, new SqlValue(1)));
+					}
+
+					break;
+			}
+
+			if (l is SqlQuery.SearchCondition)
+				l = Convert(context, new SqlFunction(typeof(bool), "CASE", l, new SqlValue(true), new SqlValue(false)));
+
+			if (r is SqlQuery.SearchCondition)
+				r = Convert(context, new SqlFunction(typeof(bool), "CASE", r, new SqlValue(true), new SqlValue(false)));
+
+			return Convert(context, new SqlQuery.Predicate.ExprExpr(l, op, r));
+		}
+
+		#endregion
+
+		#region ConvertEnumConversion
+
+		ISqlPredicate ConvertEnumConversion(IBuildContext context, Expression left, SqlQuery.Predicate.Operator op, Expression right)
+		{
+			Expression value;
+			Expression operand;
+
+			if (left is MemberExpression)
+			{
+				operand = left;
+				value   = right;
+			}
+			else if (left.NodeType == ExpressionType.Convert && ((UnaryExpression)left).Operand is MemberExpression)
+			{
+				operand = ((UnaryExpression)left).Operand;
+				value   = right;
+			}
+			else if (right is MemberExpression)
+			{
+				operand = right;
+				value   = left;
+			}
+			else if (right.NodeType == ExpressionType.Convert && ((UnaryExpression)right).Operand is MemberExpression)
+			{
+				operand = ((UnaryExpression)right).Operand;
+				value   = left;
+			}
+			else if (left.NodeType == ExpressionType.Convert)
+			{
+				operand = ((UnaryExpression)left).Operand;
+				value   = right;
+			}
+			else
+			{
+				operand = ((UnaryExpression)right).Operand;
+				value = left;
+			}
+
+			var type = operand.Type;
+
+			if (!TypeHelper.IsEnumOrNullableEnum(type))
+				return null;
+
+			var dic = new Dictionary<object,object>();
+
+			var nullValue = MappingSchema.GetNullValue(type);
+
+			if (nullValue != null)
+				dic.Add(nullValue, null);
+
+			var mapValues = MappingSchema.GetMapValues(type);
+
+			if (mapValues != null)
+				foreach (var mv in mapValues)
+					if (!dic.ContainsKey(mv.OrigValue))
+						dic.Add(mv.OrigValue, mv.MapValues[0]);
+
+			switch (value.NodeType)
+			{
+				case ExpressionType.Constant:
+					{
+						var name = Enum.GetName(type, ((ConstantExpression)value).Value);
+
+// ReSharper disable ConditionIsAlwaysTrueOrFalse
+// ReSharper disable HeuristicUnreachableCode
+						if (name == null)
+							return null;
+// ReSharper restore HeuristicUnreachableCode
+// ReSharper restore ConditionIsAlwaysTrueOrFalse
+
+						var origValue = Enum.Parse(type, name, false);
+						var mapValue  = origValue;
+
+						if (!(operand is MemberExpression))
+						{
+							if (!dic.TryGetValue(origValue, out mapValue))
+								return null;
+						}
+
+						ISqlExpression l, r;
+
+						SqlValue sqlValue;
+
+						if (left.NodeType == ExpressionType.Convert)
+						{
+							l = ConvertToSql(context, operand, false);
+							r = sqlValue = new SqlValue(mapValue);
+						}
+						else
+						{
+							r = ConvertToSql(context, operand, false);
+							l = sqlValue = new SqlValue(mapValue);
+						}
+
+						if (operand is MemberExpression)
+						{
+							var me = (MemberExpression)operand;
+							var memberAccessor = TypeAccessor.GetAccessor(me.Member.DeclaringType)[me.Member.Name];
+							sqlValue.SetEnumConverter(memberAccessor, MappingSchema);
+						}
+
+
+						return Convert(context, new SqlQuery.Predicate.ExprExpr(l, op, r));
+					}
+
+				case ExpressionType.Convert:
+					{
+						value = ((UnaryExpression)value).Operand;
+
+						var l = ConvertToSql(context, operand, false, false);
+						var r = ConvertToSql(context, value,   false, false);
+
+						MemberAccessor memberAccessor = null;
+
+						if (operand is MemberExpression)
+						{
+							// is it even possible that operand is not MemberExpression?
+							// if no, then we can remove this two last uses of SetEnumConverter(type, map)
+							// and other depending code
+							// At least currently there is no test coverage for this method and I didn't
+							// manage to create such test
+							var me = (MemberExpression)operand;
+							memberAccessor = TypeAccessor.GetAccessor(me.Member.DeclaringType)[me.Member.Name];
+						}
+
+						if (l is SqlValueBase)
+						{
+							if (memberAccessor != null)
+							{
+								((SqlValueBase)l).SetEnumConverter(memberAccessor, MappingSchema);
+							}
+							else
+							{
+								((SqlValueBase)l).SetEnumConverter(type, MappingSchema);
+							}
+						}
+
+						if (r is SqlValueBase)
+						{
+							if (memberAccessor != null)
+							{
+								((SqlValueBase)r).SetEnumConverter(memberAccessor, MappingSchema);
+							}
+							else
+							{
+								((SqlValueBase)r).SetEnumConverter(type, MappingSchema);
+							}
+						}
+
+						return Convert(context, new SqlQuery.Predicate.ExprExpr(l, op, r));
+					}
+			}
+
+			return null;
+		}
+
+		#endregion
+
+		#region ConvertObjectNullComparison
+
+		ISqlPredicate ConvertObjectNullComparison(IBuildContext context, Expression left, Expression right, bool isEqual)
+		{
+			if (right.NodeType == ExpressionType.Constant && ((ConstantExpression)right).Value == null)
+			{
+				if (left.NodeType == ExpressionType.MemberAccess || left.NodeType == ExpressionType.Parameter)
+				{
+					var ctx = GetContext(context, left);
+
+					if (ctx != null && ctx.IsExpression(left, 0, RequestFor.Object).Result ||
+						left.NodeType == ExpressionType.Parameter && ctx.IsExpression(left, 0, RequestFor.Field).Result)
+					{
+						return new SqlQuery.Predicate.Expr(new SqlValue(!isEqual));
+					}
+				}
+			}
+
+			return null;
+		}
+
+		#endregion
+
+		#region ConvertObjectComparison
+
+		public ISqlPredicate ConvertObjectComparison(
+			ExpressionType nodeType,
+			IBuildContext  leftContext,
+			Expression     left,
+			IBuildContext  rightContext,
+			Expression     right)
+		{
+			var qsl = GetContext(leftContext,  left);
+			var qsr = GetContext(rightContext, right);
+
+			var sl = qsl != null && qsl.IsExpression(left,  0, RequestFor.Object).Result;
+			var sr = qsr != null && qsr.IsExpression(right, 0, RequestFor.Object).Result;
+
+			bool      isNull;
+			SqlInfo[] lcols;
+
+			var rmembers = new Dictionary<MemberInfo,Expression>(new MemberInfoComparer());
+
+			if (sl == false && sr == false)
+			{
+				var lmembers = new Dictionary<MemberInfo,Expression>(new MemberInfoComparer());
+
+				if (!ProcessProjection(lmembers, left) && !ProcessProjection(rmembers, right))
+					return null;
+
+				if (lmembers.Count == 0)
+				{
+					var r = right;
+					right = left;
+					left  = r;
+
+					var c = rightContext;
+					rightContext = leftContext;
+					leftContext  = c;
+
+					var q = qsr;
+					qsl = q;
+
+					sr = false;
+
+					var lm = lmembers;
+					lmembers = rmembers;
+					rmembers = lm;
+				}
+
+				isNull = right is ConstantExpression && ((ConstantExpression)right).Value == null;
+				lcols  = lmembers.Select(m => new SqlInfo(m.Key) { Sql = ConvertToSql(leftContext, m.Value, false) }).ToArray();
+			}
+			else
+			{
+				if (sl == false)
+				{
+					var r = right;
+					right = left;
+					left  = r;
+
+					var c = rightContext;
+					rightContext = leftContext;
+					leftContext  = c;
+
+					var q = qsr;
+					qsl = q;
+
+					sr = false;
+				}
+
+				isNull = right is ConstantExpression && ((ConstantExpression)right).Value == null;
+				lcols  = qsl.ConvertToSql(left, 0, ConvertFlags.Key);
+
+				if (!sr)
+					ProcessProjection(rmembers, right);
+			}
+
+			if (lcols.Length == 0)
+				return null;
+
+			var condition = new SqlQuery.SearchCondition();
+
+			foreach (var lcol in lcols)
+			{
+				if (lcol.Members.Count == 0)
+					throw new InvalidOperationException();
+
+				ISqlExpression rcol = null;
+
+				var lmember = lcol.Members[lcol.Members.Count - 1];
+
+				if (sr)
+				{
+					var info = rightContext.ConvertToSql(Expression.MakeMemberAccess(right, lmember), 0, ConvertFlags.Field).Single();
+					rcol = info.Sql;
+				}
+				else
+				{
+					if (rmembers.Count != 0)
+					{
+						var info = rightContext.ConvertToSql(rmembers[lmember], 0, ConvertFlags.Field)[0];
+						rcol = info.Sql;
+					}
+				}
+
+				var rex =
+					isNull ?
+						new SqlValue(right.Type, null) :
+						rcol ?? GetParameter(right, lmember);
+
+				var predicate = Convert(leftContext, new SqlQuery.Predicate.ExprExpr(
+					lcol.Sql,
+					nodeType == ExpressionType.Equal ? SqlQuery.Predicate.Operator.Equal : SqlQuery.Predicate.Operator.NotEqual,
+					rex));
+
+				condition.Conditions.Add(new SqlQuery.Condition(false, predicate));
+			}
+
+			if (nodeType == ExpressionType.NotEqual)
+				foreach (var c in condition.Conditions)
+					c.IsOr = true;
+
+			return condition;
+		}
+
+		ISqlPredicate ConvertNewObjectComparison(IBuildContext context, ExpressionType nodeType, Expression left, Expression right)
+		{
+			left  = FindExpression(left);
+			right = FindExpression(right);
+
+			var condition = new SqlQuery.SearchCondition();
+
+			if (left.NodeType != ExpressionType.New)
+			{
+				var temp = left;
+				left  = right;
+				right = temp;
+			}
+
+			var newRight = right as NewExpression;
+			var newExpr  = (NewExpression)left;
+
+// ReSharper disable ConditionIsAlwaysTrueOrFalse
+// ReSharper disable HeuristicUnreachableCode
+			if (newExpr.Members == null)
+				return null;
+// ReSharper restore HeuristicUnreachableCode
+// ReSharper restore ConditionIsAlwaysTrueOrFalse
+
+			for (var i = 0; i < newExpr.Arguments.Count; i++)
+			{
+				var lex = ConvertToSql(context, newExpr.Arguments[i], false);
+				var rex =
+					newRight != null ?
+						ConvertToSql(context, newRight.Arguments[i], false) :
+						GetParameter(right, newExpr.Members[i]);
+
+				var predicate = Convert(context,
+					new SqlQuery.Predicate.ExprExpr(
+						lex,
+						nodeType == ExpressionType.Equal ? SqlQuery.Predicate.Operator.Equal : SqlQuery.Predicate.Operator.NotEqual,
+						rex));
+
+				condition.Conditions.Add(new SqlQuery.Condition(false, predicate));
+			}
+
+			if (nodeType == ExpressionType.NotEqual)
+				foreach (var c in condition.Conditions)
+					c.IsOr = true;
+
+			return condition;
+		}
+
+		ISqlExpression GetParameter(Expression ex, MemberInfo member)
+		{
+			if (member is MethodInfo)
+				member = TypeHelper.GetPropertyByMethod((MethodInfo)member);
+
+			var par    = ReplaceParameter(_expressionAccessors, ex, _ => {});
+			var expr   = Expression.MakeMemberAccess(par.Type == typeof(object) ? Expression.Convert(par, member.DeclaringType) : par, member);
+			var mapper = Expression.Lambda<Func<Expression,object[],object>>(
+				Expression.Convert(expr, typeof(object)),
+				new [] { ExpressionParam, ParametersParam });
+
+			var p = new ParameterAccessor
+			{
+				Expression   = expr,
+				Accessor     = mapper.Compile(),
+				SqlParameter = new SqlParameter(expr.Type, member.Name, null, MappingSchema)
+			};
+
+			_parameters.Add(expr, p);
+			CurrentSqlParameters.Add(p);
+
+			return p.SqlParameter;
+		}
+
+		static Expression FindExpression(Expression expr)
+		{
+			var ret = expr.Find(pi =>
+			{
+				switch (pi.NodeType)
+				{
+					case ExpressionType.Convert      :
+						{
+							var e = (UnaryExpression)expr;
+
+							return
+								e.Operand.NodeType == ExpressionType.ArrayIndex &&
+								((BinaryExpression)e.Operand).Left == ParametersParam;
+						}
+
+					case ExpressionType.MemberAccess :
+					case ExpressionType.New          :
+						return true;
+				}
+
+				return false;
+			});
+
+			if (ret == null)
+				throw new InvalidOperationException();
+
+			return ret;
+		}
+
+		#endregion
+
+		#region ConvertInPredicate
+
+		private ISqlPredicate ConvertInPredicate(IBuildContext context, MethodCallExpression expression)
+		{
+			var e        = expression;
+			var argIndex = e.Object != null ? 0 : 1;
+			var arr      = e.Object ?? e.Arguments[0];
+			var arg      = e.Arguments[argIndex];
+
+			ISqlExpression expr = null;
+
+			var ctx = GetContext(context, arg);
+
+			if (ctx is TableBuilder.TableContext &&
+			    ctx.SqlQuery != context.SqlQuery &&
+			    ctx.IsExpression(arg, 0, RequestFor.Object).Result)
+			{
+				expr = ctx.SqlQuery;
+			}
+
+			if (expr == null)
+			{
+				var sql = ConvertExpressions(context, arg, ConvertFlags.Key);
+
+				if (sql.Length == 1 && sql[0].Members.Count == 0)
+					expr = sql[0].Sql;
+				else
+					expr = new SqlExpression(
+						'\x1' + string.Join(",", sql.Select(s => s.Members[s.Members.Count - 1].Name).ToArray()),
+						sql.Select(s => s.Sql).ToArray());
+			}
+
+			MemberAccessor memberAccessor = null;
+
+			if (arg is MemberExpression)
+			{
+				var me = (MemberExpression)arg;
+				if (TypeHelper.IsEnumOrNullableEnum(me.Type))
+				{
+					memberAccessor = TypeAccessor.GetAccessor(me.Member.DeclaringType)[me.Member.Name];
+				}
+			}
+			
+			switch (arr.NodeType)
+			{
+				case ExpressionType.NewArrayInit :
+					{
+						var newArr = (NewArrayExpression)arr;
+
+						if (newArr.Expressions.Count == 0)
+							return new SqlQuery.Predicate.Expr(new SqlValue(false));
+
+						var exprs  = new ISqlExpression[newArr.Expressions.Count];
+
+						for (var i = 0; i < newArr.Expressions.Count; i++)
+						{
+							exprs[i] = ConvertToSql(context, newArr.Expressions[i], false, false);
+
+							if (memberAccessor != null && exprs[i] is SqlValue)
+							{
+								((SqlValue)exprs[i]).SetEnumConverter(memberAccessor, MappingSchema);
+							}
+						}
+
+						return new SqlQuery.Predicate.InList(expr, false, exprs);
+					}
+
+				default :
+
+					if (CanBeCompiled(arr))
+					{
+						var p = BuildParameter(arr).SqlParameter;
+						p.IsQueryParameter = false;
+						if (memberAccessor != null)
+						{
+							p.SetEnumConverter(memberAccessor, MappingSchema);
+						}
+						return new SqlQuery.Predicate.InList(expr, false, p);
+					}
+
+					break;
+			}
+
+			throw new LinqException("'{0}' cannot be converted to SQL.", expression);
+		}
+
+		#endregion
+
+		#region LIKE predicate
+
+		ISqlPredicate ConvertLikePredicate(IBuildContext context, MethodCallExpression expression, string start, string end)
+		{
+			var e = expression;
+			var o = ConvertToSql(context, e.Object,       false);
+			var a = ConvertToSql(context, e.Arguments[0], false);
+
+			if (a is SqlValue)
+			{
+				var value = ((SqlValue)a).Value;
+
+				if (value == null)
+					throw new LinqException("NULL cannot be used as a LIKE predicate parameter.");
+
+				return value.ToString().IndexOfAny(new[] { '%', '_' }) < 0?
+					new SqlQuery.Predicate.Like(o, false, new SqlValue(start + value + end), null):
+					new SqlQuery.Predicate.Like(o, false, new SqlValue(start + EscapeLikeText(value.ToString()) + end), new SqlValue('~'));
+			}
+
+			if (a is SqlParameter)
+			{
+				var p  = (SqlParameter)a;
+				var ep = (from pm in CurrentSqlParameters where pm.SqlParameter == p select pm).First();
+
+				ep = new ParameterAccessor
+				{
+					Expression   = ep.Expression,
+					Accessor     = ep.Accessor,
+					SqlParameter = new SqlParameter(ep.Expression.Type, p.Name, p.Value, GetLikeEscaper(start, end))
+				};
+
+				CurrentSqlParameters.Add(ep);
+
+				return new SqlQuery.Predicate.Like(o, false, ep.SqlParameter, new SqlValue('~'));
+			}
+
+			var mi = ReflectionHelper.Expressor<string>.MethodExpressor(_ => _.Replace("", ""));
+			var ex =
+				Expression.Call(
+				Expression.Call(
+				Expression.Call(
+					e.Arguments[0],
+						mi, Expression.Constant("~"), Expression.Constant("~~")),
+						mi, Expression.Constant("%"), Expression.Constant("~%")),
+						mi, Expression.Constant("_"), Expression.Constant("~_"));
+
+			var expr = ConvertToSql(context, ConvertExpression(ex), false);
+
+			if (!string.IsNullOrEmpty(start))
+				expr = new SqlBinaryExpression(typeof(string), new SqlValue("%"), "+", expr);
+
+			if (!string.IsNullOrEmpty(end))
+				expr = new SqlBinaryExpression(typeof(string), expr, "+", new SqlValue("%"));
+
+			return new SqlQuery.Predicate.Like(o, false, expr, new SqlValue('~'));
+		}
+
+		ISqlPredicate ConvertLikePredicate(IBuildContext context, MethodCallExpression expression)
+		{
+			var e  = expression;
+			var a1 = ConvertToSql(context, e.Arguments[0], false);
+			var a2 = ConvertToSql(context, e.Arguments[1], false);
+
+			ISqlExpression a3 = null;
+
+			if (e.Arguments.Count == 3)
+				a3 = ConvertToSql(context, e.Arguments[2], false);
+
+			return new SqlQuery.Predicate.Like(a1, false, a2, a3);
+		}
+
+		static string EscapeLikeText(string text)
+		{
+			if (text.IndexOfAny(new[] { '%', '_' }) < 0)
+				return text;
+
+			var builder = new StringBuilder(text.Length);
+
+			foreach (var ch in text)
+			{
+				switch (ch)
+				{
+					case '%':
+					case '_':
+					case '~':
+						builder.Append('~');
+						break;
+				}
+
+				builder.Append(ch);
+			}
+
+			return builder.ToString();
+		}
+
+		static Converter<object,object> GetLikeEscaper(string start, string end)
+		{
+			return value => value == null? null: start + EscapeLikeText(value.ToString()) + end;
+		}
+
+		#endregion
+
+		#region MakeIsPredicate
+
+		internal ISqlPredicate MakeIsPredicate(TableBuilder.TableContext table, Type typeOperand)
+		{
+			if (typeOperand == table.ObjectType && !table.InheritanceMapping.Any(m => m.Type == typeOperand))
+				return Convert(table, new SqlQuery.Predicate.Expr(new SqlValue(true)));
+
+			return MakeIsPredicate(
+				table, table.InheritanceMapping, table.InheritanceDiscriminators, typeOperand,
+				name => table.SqlTable.Fields.Values.First(f => f.Name == name));
+		}
+
+		internal ISqlPredicate MakeIsPredicate(
+			IBuildContext                     context,
+			List<InheritanceMappingAttribute> inheritanceMapping,
+			List<string>                      inheritanceDiscriminators,
+			Type                              toType,
+			Func<string,ISqlExpression>       getSql)
+		{
+			var mapping = inheritanceMapping
+				.Select((m,i) => new { m, i })
+				.Where ( m => m.m.Type == toType && !m.m.IsDefault)
+				.ToList();
+
+			switch (mapping.Count)
+			{
+				case 0 :
+					{
+						var cond = new SqlQuery.SearchCondition();
+
+						foreach (var m in inheritanceMapping.Select((m,i) => new { m, i }).Where(m => !m.m.IsDefault))
+						{
+							cond.Conditions.Add(
+								new SqlQuery.Condition(
+									false, 
+									Convert(context,
+										new SqlQuery.Predicate.ExprExpr(
+											getSql(inheritanceDiscriminators[m.i]),
+											SqlQuery.Predicate.Operator.NotEqual,
+											new SqlValue(m.m.Code)))));
+						}
+
+						return cond;
+					}
+
+				case 1 :
+					return Convert(context,
+						new SqlQuery.Predicate.ExprExpr(
+							getSql(inheritanceDiscriminators[mapping[0].i]),
+							SqlQuery.Predicate.Operator.Equal,
+							new SqlValue(mapping[0].m.Code)));
+
+				default:
+					{
+						var cond = new SqlQuery.SearchCondition();
+
+						foreach (var m in mapping)
+						{
+							cond.Conditions.Add(
+								new SqlQuery.Condition(
+									false,
+									Convert(context,
+										new SqlQuery.Predicate.ExprExpr(
+											getSql(inheritanceDiscriminators[m.i]),
+											SqlQuery.Predicate.Operator.Equal,
+											new SqlValue(m.m.Code))),
+									true));
+						}
+
+						return cond;
+					}
+			}
+		}
+
+		ISqlPredicate MakeIsPredicate(IBuildContext context, TypeBinaryExpression expression)
+		{
+			var typeOperand = expression.TypeOperand;
+			var table       = new TableBuilder.TableContext(this, new BuildInfo((IBuildContext)null, Expression.Constant(null), new SqlQuery()), typeOperand);
+
+			if (typeOperand == table.ObjectType && !table.InheritanceMapping.Any(m => m.Type == typeOperand))
+				return Convert(table, new SqlQuery.Predicate.Expr(new SqlValue(true)));
+
+			var mapping = table.InheritanceMapping.Select((m,i) => new { m, i }).Where(m => m.m.Type == typeOperand && !m.m.IsDefault).ToList();
+			var isEqual = true;
+
+			if (mapping.Count == 0)
+			{
+				mapping = table.InheritanceMapping.Select((m,i) => new { m, i }).Where(m => !m.m.IsDefault).ToList();
+				isEqual = false;
+			}
+
+			Expression expr = null;
+
+			foreach (var m in mapping)
+			{
+				var field = table.SqlTable.Fields[table.InheritanceDiscriminators[m.i]];
+				var ttype = field.MemberMapper.MemberAccessor.TypeAccessor.OriginalType;
+				var obj   = expression.Expression;
+
+				if (obj.Type != ttype)
+					obj = Expression.Convert(expression.Expression, ttype);
+
+				var left = Expression.PropertyOrField(obj, field.Name);
+				var code = m.m.Code;
+
+				if (code == null)
+					code = TypeHelper.GetDefaultValue(left.Type);
+				else if (left.Type != code.GetType())
+					code = MappingSchema.ConvertChangeType(code, left.Type);
+
+				Expression right = Expression.Constant(code, left.Type);
+
+				var e = isEqual ? Expression.Equal(left, right) : Expression.NotEqual(left, right);
+
+				expr = expr != null ? Expression.AndAlso(expr, e) : e;
+			}
+
+			return ConvertPredicate(context, expr);
+		}
+
+		#endregion
+
+		#endregion
+
+		#region Search Condition Builder
+
+		void BuildSearchCondition(IBuildContext context, Expression expression, List<SqlQuery.Condition> conditions)
+		{
+			switch (expression.NodeType)
+			{
+				case ExpressionType.And     :
+				case ExpressionType.AndAlso :
+					{
+						var e = (BinaryExpression)expression;
+
+						BuildSearchCondition(context, e.Left,  conditions);
+						BuildSearchCondition(context, e.Right, conditions);
+
+						break;
+					}
+
+				case ExpressionType.Or     :
+				case ExpressionType.OrElse :
+					{
+						var e           = (BinaryExpression)expression;
+						var orCondition = new SqlQuery.SearchCondition();
+
+						BuildSearchCondition(context, e.Left,  orCondition.Conditions);
+						orCondition.Conditions[orCondition.Conditions.Count - 1].IsOr = true;
+						BuildSearchCondition(context, e.Right, orCondition.Conditions);
+
+						conditions.Add(new SqlQuery.Condition(false, orCondition));
+
+						break;
+					}
+
+				case ExpressionType.Not    :
+					{
+						var e            = expression as UnaryExpression;
+						var notCondition = new SqlQuery.SearchCondition();
+
+						BuildSearchCondition(context, e.Operand, notCondition.Conditions);
+
+						if (notCondition.Conditions.Count == 1 && notCondition.Conditions[0].Predicate is SqlQuery.Predicate.NotExpr)
+						{
+							var p = notCondition.Conditions[0].Predicate as SqlQuery.Predicate.NotExpr;
+							p.IsNot = !p.IsNot;
+							conditions.Add(notCondition.Conditions[0]);
+						}
+						else
+							conditions.Add(new SqlQuery.Condition(true, notCondition));
+
+						break;
+					}
+
+				default                    :
+					var predicate = ConvertPredicate(context, expression);
+
+					if (predicate is SqlQuery.Predicate.Expr)
+					{
+						var expr = ((SqlQuery.Predicate.Expr)predicate).Expr1;
+
+						if (expr.ElementType == QueryElementType.SearchCondition)
+						{
+							var sc = (SqlQuery.SearchCondition)expr;
+
+							if (sc.Conditions.Count == 1)
+							{
+								conditions.Add(sc.Conditions[0]);
+								break;
+							}
+						}
+					}
+
+					conditions.Add(new SqlQuery.Condition(false, predicate));
+
+					break;
+			}
+		}
+
+		#endregion
+
+		#region CanBeTranslatedToSql
+
+		bool CanBeTranslatedToSql(IBuildContext context, Expression expr, bool canBeCompiled)
+		{
+			List<Expression> ignoredMembers = null;
+
+			return null == expr.Find(pi =>
+			{
+				if (ignoredMembers != null)
+				{
+					if (pi != ignoredMembers[ignoredMembers.Count - 1])
+						throw new InvalidOperationException();
+
+					if (ignoredMembers.Count == 1)
+						ignoredMembers = null;
+					else
+						ignoredMembers.RemoveAt(ignoredMembers.Count - 1);
+
+					return false;
+				}
+
+				switch (pi.NodeType)
+				{
+					case ExpressionType.MemberAccess :
+						{
+							var ma   = (MemberExpression)pi;
+							var attr = GetFunctionAttribute(ma.Member);
+
+							if (attr == null && !TypeHelper.IsNullableValueMember(ma.Member))
+							{
+								if (canBeCompiled)
+								{
+									var ctx = GetContext(context, pi);
+
+									if (ctx == null)
+										return !CanBeCompiled(pi);
+
+									if (ctx.IsExpression(pi, 0, RequestFor.Object).Result)
+										return !CanBeCompiled(pi);
+
+									ignoredMembers = ma.Expression.GetMembers();
+								}
+							}
+
+							break;
+						}
+
+					case ExpressionType.Parameter    :
+						{
+							var ctx = GetContext(context, pi);
+
+							if (ctx == null)
+								if (canBeCompiled)
+									return !CanBeCompiled(pi);
+
+							break;
+						}
+
+					case ExpressionType.Call         :
+						{
+							var e = (MethodCallExpression)pi;
+
+							if (e.Method.DeclaringType != typeof(Enumerable))
+							{
+								var attr = GetFunctionAttribute(e.Method);
+
+								if (attr == null && canBeCompiled)
+									return !CanBeCompiled(pi);
+							}
+
+							break;
+						}
+
+					case ExpressionType.TypeIs       : return canBeCompiled;
+					case ExpressionType.TypeAs       :
+					case ExpressionType.New          : return true;
+
+					case ExpressionType.NotEqual     :
+					case ExpressionType.Equal        :
+						{
+							var e = (BinaryExpression)pi;
+
+							Expression obj = null;
+
+							if (e.Left.NodeType == ExpressionType.Constant && ((ConstantExpression)e.Left).Value == null)
+								obj = e.Right;
+							else if (e.Right.NodeType == ExpressionType.Constant && ((ConstantExpression)e.Right).Value == null)
+								obj = e.Left;
+
+							if (obj != null)
+							{
+								var ctx = GetContext(context, obj);
+
+								if (ctx != null)
+								{
+									if (ctx.IsExpression(obj, 0, RequestFor.Table).      Result ||
+									    ctx.IsExpression(obj, 0, RequestFor.Association).Result)
+									{
+										ignoredMembers = obj.GetMembers();
+									}
+								}
+							}
+
+							break;
+						}
+				}
+
+				return false;
+			});
+		}
+
+		#endregion
+
+		#region Helpers
+
+		public IBuildContext GetContext([JetBrains.Annotations.NotNull] IBuildContext current, Expression expression)
+		{
+			var root = expression.GetRootObject();
+
+			for (; current != null; current = current.Parent)
+				if (current.IsExpression(root, 0, RequestFor.Root).Result)
+					return current;
+
+			return null;
+		}
+
+		SqlFunctionAttribute GetFunctionAttribute(ICustomAttributeProvider member)
+		{
+			var attrs = member.GetCustomAttributes(typeof(SqlFunctionAttribute), true);
+
+			if (attrs.Length == 0)
+				return null;
+
+			SqlFunctionAttribute attr = null;
+
+			foreach (SqlFunctionAttribute a in attrs)
+			{
+				if (a.SqlProvider == SqlProvider.Name)
+				{
+					attr = a;
+					break;
+				}
+
+				if (a.SqlProvider == null)
+					attr = a;
+			}
+
+			return attr;
+		}
+
+		internal TableFunctionAttribute GetTableFunctionAttribute(ICustomAttributeProvider member)
+		{
+			var attrs = member.GetCustomAttributes(typeof(TableFunctionAttribute), true);
+
+			if (attrs.Length == 0)
+				return null;
+
+			TableFunctionAttribute attr = null;
+
+			foreach (TableFunctionAttribute a in attrs)
+			{
+				if (a.SqlProvider == SqlProvider.Name)
+				{
+					attr = a;
+					break;
+				}
+
+				if (a.SqlProvider == null)
+					attr = a;
+			}
+
+			return attr;
+		}
+
+		public ISqlExpression Convert(IBuildContext context, ISqlExpression expr)
+		{
+			SqlProvider.SqlQuery = context.SqlQuery;
+			return SqlProvider.ConvertExpression(expr);
+		}
+
+		public ISqlPredicate Convert(IBuildContext context, ISqlPredicate predicate)
+		{
+			SqlProvider.SqlQuery = context.SqlQuery;
+			return SqlProvider.ConvertPredicate(predicate);
+		}
+
+		public ISqlExpression ConvertTimeSpanMember(IBuildContext context, MemberExpression expression)
+		{
+			if (expression.Member.DeclaringType == typeof(TimeSpan))
+			{
+				switch (expression.Expression.NodeType)
+				{
+					case ExpressionType.Subtract       :
+					case ExpressionType.SubtractChecked:
+
+						Sql.DateParts datePart;
+
+						switch (expression.Member.Name)
+						{
+							case "TotalMilliseconds" : datePart = Sql.DateParts.Millisecond; break;
+							case "TotalSeconds"      : datePart = Sql.DateParts.Second;      break;
+							case "TotalMinutes"      : datePart = Sql.DateParts.Minute;      break;
+							case "TotalHours"        : datePart = Sql.DateParts.Hour;        break;
+							case "TotalDays"         : datePart = Sql.DateParts.Day;         break;
+							default                  : return null;
+						}
+
+						var e = (BinaryExpression)expression.Expression;
+
+						return new SqlFunction(
+							typeof(int),
+							"DateDiff",
+							new SqlValue(datePart),
+							ConvertToSql(context, e.Right, false),
+							ConvertToSql(context, e.Left,  false));
+				}
+			}
+
+			return null;
+		}
+
+		internal ISqlExpression ConvertSearchCondition(IBuildContext context, ISqlExpression sqlExpression)
+		{
+			if (sqlExpression is SqlQuery.SearchCondition)
+			{
+				if (sqlExpression.CanBeNull())
+				{
+					var notExpr = new SqlQuery.SearchCondition
+					{
+						Conditions = { new SqlQuery.Condition(true, new SqlQuery.Predicate.Expr(sqlExpression, sqlExpression.Precedence)) }
+					};
+
+					return Convert(context, new SqlFunction(sqlExpression.SystemType, "CASE", sqlExpression, new SqlValue(1), notExpr, new SqlValue(0), new SqlValue(null)));
+				}
+
+				return Convert(context, new SqlFunction(sqlExpression.SystemType, "CASE", sqlExpression, new SqlValue(1), new SqlValue(0)));
+			}
+
+			return sqlExpression;
+		}
+
+		public bool ProcessProjection(Dictionary<MemberInfo,Expression> members, Expression expression)
+		{
+			switch (expression.NodeType)
+			{
+				// new { ... }
+				//
+				case ExpressionType.New        :
+					{
+						var expr = (NewExpression)expression;
+
+// ReSharper disable ConditionIsAlwaysTrueOrFalse
+// ReSharper disable HeuristicUnreachableCode
+						if (expr.Members == null)
+							return false;
+// ReSharper restore HeuristicUnreachableCode
+// ReSharper restore ConditionIsAlwaysTrueOrFalse
+
+						for (var i = 0; i < expr.Members.Count; i++)
+						{
+							var member = expr.Members[i];
+
+							members.Add(member, expr.Arguments[i]);
+
+							if (member is MethodInfo)
+								members.Add(TypeHelper.GetPropertyByMethod((MethodInfo)member), expr.Arguments[i]);
+						}
+
+						return true;
+					}
+
+				// new MyObject { ... }
+				//
+				case ExpressionType.MemberInit :
+					{
+						var expr = (MemberInitExpression)expression;
+						var dic  = TypeAccessor.GetAccessor(expr.Type)
+							.Select((m,i) => new { m, i })
+							.ToDictionary(_ => _.m.MemberInfo.Name, _ => _.i);
+
+						foreach (var binding in expr.Bindings.Cast<MemberAssignment>().OrderBy(b => dic.ContainsKey(b.Member.Name) ? dic[b.Member.Name] : 1000000))
+						{
+							members.Add(binding.Member, binding.Expression);
+
+							if (binding.Member is MethodInfo)
+								members.Add(TypeHelper.GetPropertyByMethod((MethodInfo)binding.Member), binding.Expression);
+						}
+
+						return true;
+					}
+
+				// .Select(p => everything else)
+				//
+				default                        :
+					return false;
+			}
+		}
+
+		public void ReplaceParent(IBuildContext oldParent, IBuildContext newParent)
+		{
+			foreach (var context in Contexts)
+				if (context != newParent)
+					if (context.Parent == oldParent)
+						context.Parent = newParent;
+		}
+
+		#endregion
+	}
+}