diff Source/Data/Linq/Builder/SelectContext.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/Data/Linq/Builder/SelectContext.cs	Thu Mar 27 21:46:09 2014 +0400
@@ -0,0 +1,1081 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+
+namespace BLToolkit.Data.Linq.Builder
+{
+	using BLToolkit.Linq;
+	using Data.Sql;
+	using Reflection;
+
+	// This class implements double functionality (scalar and member type selects)
+	// and could be implemented as two different classes.
+	// But the class means to have a lot of inheritors, and functionality of the inheritors
+	// will be doubled as well. So lets double it once here.
+	//
+	public class SelectContext : IBuildContext
+	{
+		#region Init
+
+#if DEBUG
+		[CLSCompliant(false)]
+		public string _sqlQueryText { get { return SqlQuery == null ? "" : SqlQuery.SqlText; } }
+
+		public MethodCallExpression MethodCall;
+#endif
+
+		public IBuildContext[]   Sequence { get; set; }
+		public LambdaExpression  Lambda   { get; set; }
+		public Expression        Body     { get; set; }
+		public ExpressionBuilder Builder  { get; private set; }
+		public SqlQuery          SqlQuery { get; set; }
+		public IBuildContext     Parent   { get; set; }
+		public bool              IsScalar { get; private set; }
+
+		Expression IBuildContext.Expression { get { return Lambda; } }
+
+		public readonly Dictionary<MemberInfo,Expression> Members = new Dictionary<MemberInfo,Expression>(new MemberInfoComparer());
+
+		public SelectContext(IBuildContext parent, LambdaExpression lambda, params IBuildContext[] sequences)
+		{
+			Parent   = parent;
+			Sequence = sequences;
+			Builder  = sequences[0].Builder;
+			Lambda   = lambda;
+			Body     = lambda.Body;
+			SqlQuery = sequences[0].SqlQuery;
+
+			foreach (var context in Sequence)
+				context.Parent = this;
+
+			IsScalar = !Builder.ProcessProjection(Members, Body);
+
+			Builder.Contexts.Add(this);
+		}
+
+		#endregion
+
+		#region BuildQuery
+
+		public virtual void BuildQuery<T>(Query<T> query, ParameterExpression queryParameter)
+		{
+			var expr   = BuildExpression(null, 0);
+			var mapper = Builder.BuildMapper<T>(expr);
+
+			query.SetQuery(mapper.Compile());
+		}
+
+		#endregion
+
+		#region BuildExpression
+
+		public virtual Expression BuildExpression(Expression expression, int level)
+		{
+			{
+				var key = Tuple.Create(expression, level, ConvertFlags.Field);
+
+				SqlInfo[] info;
+
+				if (_expressionIndex.TryGetValue(key, out info))
+				{
+					var idx  = Parent == null ? info[0].Index : Parent.ConvertToParentIndex(info[0].Index, this);
+					return Builder.BuildSql((expression ?? Body).Type, idx);
+				}
+			}
+
+			if (expression == null)
+				return Builder.BuildExpression(this, Body);
+
+			var levelExpression = expression.GetLevelExpression(level);
+
+			if (IsScalar)
+			{
+				if (Body.NodeType != ExpressionType.Parameter && level == 0)
+					if (levelExpression == expression)
+						if (IsSubQuery() && IsExpression(null, 0, RequestFor.Expression).Result)
+						{
+							var info = ConvertToIndex(expression, level, ConvertFlags.Field).Single();
+							var idx = Parent == null ? info.Index : Parent.ConvertToParentIndex(info.Index, this);
+
+							return Builder.BuildSql(expression.Type, idx);
+						}
+
+				return ProcessScalar(
+					expression,
+					level,
+					(ctx, ex, l) => ctx.BuildExpression(ex, l),
+					() => GetSequence(expression, level).BuildExpression(null, 0));
+			}
+			else
+			{
+				if (level == 0)
+				{
+					var sequence = GetSequence(expression, level);
+
+					return levelExpression == expression ?
+						sequence.BuildExpression(null,       0) :
+						sequence.BuildExpression(expression, level + 1);
+				}
+
+				switch (levelExpression.NodeType)
+				{
+					case ExpressionType.MemberAccess :
+						{
+							var memberExpression = GetMemberExpression(
+								((MemberExpression)levelExpression).Member,
+								levelExpression == expression,
+								levelExpression.Type);
+
+							if (levelExpression == expression)
+							{
+								if (IsSubQuery())
+								{
+									switch (memberExpression.NodeType)
+									{
+										case ExpressionType.New        :
+										case ExpressionType.MemberInit :
+											{
+												return memberExpression.Convert(e =>
+												{
+													if (e != memberExpression)
+													{
+														switch (e.NodeType)
+														{
+															case ExpressionType.MemberAccess :
+																var sequence = GetSequence(memberExpression, 0);
+
+																if (sequence != null &&
+																	!sequence.IsExpression(e, 0, RequestFor.Object).Result &&
+																	!sequence.IsExpression(e, 0, RequestFor.Field). Result)
+																{
+																	var info = ConvertToIndex(e, 0, ConvertFlags.Field).Single();
+																	var idx  = Parent == null ? info.Index : Parent.ConvertToParentIndex(info.Index, this);
+
+																	return Builder.BuildSql(e.Type, idx);
+																}
+
+																return Builder.BuildExpression(this, e);
+														}
+													}
+
+													return e;
+												});
+											}
+									}
+
+									var me = memberExpression.NodeType == ExpressionType.Parameter ? null : memberExpression;
+
+									if (!IsExpression(me, 0, RequestFor.Object).Result &&
+										!IsExpression(me, 0, RequestFor.Field). Result)
+									{
+										var info = ConvertToIndex(expression, level, ConvertFlags.Field).Single();
+										var idx  = Parent == null ? info.Index : Parent.ConvertToParentIndex(info.Index, this);
+
+										return Builder.BuildSql(expression.Type, idx);
+									}
+								}
+
+								return Builder.BuildExpression(this, memberExpression);
+							}
+
+							{
+								var sequence = GetSequence(expression, level);
+
+								switch (memberExpression.NodeType)
+								{
+									case ExpressionType.Parameter  :
+										{
+											var parameter = Lambda.Parameters[Sequence.Length == 0 ? 0 : Array.IndexOf(Sequence, sequence)];
+										
+											if (memberExpression == parameter)
+												return sequence.BuildExpression(expression, level + 1);
+
+											break;
+										
+										}
+
+									case ExpressionType.New        :
+									case ExpressionType.MemberInit :
+										{
+											var mmExpresion = GetMemberExpression(memberExpression, expression, level + 1);
+											return Builder.BuildExpression(this, mmExpresion);
+										}
+								}
+
+								var expr = expression.Convert(ex => ex == levelExpression ? memberExpression : ex);
+
+								return sequence.BuildExpression(expr, 1);
+							}
+						}
+
+					case ExpressionType.Parameter :
+						break;
+				}
+			}
+
+			throw new InvalidOperationException();
+		}
+
+		#endregion
+
+		#region ConvertToSql
+
+		readonly Dictionary<MemberInfo,SqlInfo[]> _sql = new Dictionary<MemberInfo,SqlInfo[]>(new MemberInfoComparer());
+
+		public virtual SqlInfo[] ConvertToSql(Expression expression, int level, ConvertFlags flags)
+		{
+			if (expression != null && level > 0 && expression.NodeType == ExpressionType.Call)
+			{
+				var e = (MethodCallExpression)expression;
+
+				if (e.Method.DeclaringType == typeof(Enumerable))
+				{
+					return new[] { new SqlInfo { Sql = Builder.SubQueryToSql(this, e) } };
+				}
+			}
+
+			if (IsScalar)
+			{
+				if (expression == null)
+					return Builder.ConvertExpressions(this, Body, flags);
+
+				switch (flags)
+				{
+					case ConvertFlags.Field :
+					case ConvertFlags.Key   :
+					case ConvertFlags.All   :
+						{
+							if (Body.NodeType != ExpressionType.Parameter && level == 0)
+							{
+								var levelExpression = expression.GetLevelExpression(level);
+
+								if (levelExpression != expression)
+									if (flags != ConvertFlags.Field && IsExpression(expression, level, RequestFor.Field).Result)
+										flags = ConvertFlags.Field;
+							}
+
+							return ProcessScalar(
+								expression,
+								level,
+								(ctx, ex, l) => ctx.ConvertToSql(ex, l, flags),
+								() => new[] { new SqlInfo { Sql = Builder.ConvertToSql(this, expression, false) } });
+						}
+				}
+			}
+			else
+			{
+				if (expression == null)
+				{
+					if (flags != ConvertFlags.Field)
+					{
+						var q =
+							from m in Members
+							where !(m.Key is MethodInfo)
+							select ConvertMember(m.Key, m.Value, flags) into mm
+							from m in mm
+							select m;
+
+						return q.ToArray();
+					}
+
+					throw new InvalidOperationException();
+				}
+
+				switch (flags)
+				{
+					case ConvertFlags.All   :
+					case ConvertFlags.Key   :
+					case ConvertFlags.Field :
+						{
+							var levelExpression = expression.GetLevelExpression(level);
+
+							switch (levelExpression.NodeType)
+							{
+								case ExpressionType.MemberAccess :
+									{
+										if (level != 0 && levelExpression == expression)
+										{
+											var member = ((MemberExpression)levelExpression).Member;
+
+											SqlInfo[] sql;
+
+											if (!_sql.TryGetValue(member, out sql))
+											{
+												var memberExpression = GetMemberExpression(
+													member, levelExpression == expression, levelExpression.Type);
+
+												sql = ConvertExpressions(memberExpression, flags)
+													.Select(si => si.Clone(member)).ToArray();
+
+												_sql.Add(member, sql);
+											}
+
+											return sql;
+										}
+
+										return ProcessMemberAccess(
+											expression, (MemberExpression)levelExpression, level,
+											(n,ctx,ex,l,mex) =>
+											{
+												switch (n)
+												{
+													case 0 :
+														var buildExpression = GetExpression(expression, levelExpression, mex);
+														return ConvertExpressions(buildExpression, flags);
+													default:
+														return ctx.ConvertToSql(ex, l, flags);
+												}
+											});
+									}
+
+								case ExpressionType.Parameter:
+									if (levelExpression != expression)
+										return GetSequence(expression, level).ConvertToSql(expression, level + 1, flags);
+
+									if (level == 0)
+										return GetSequence(expression, level).ConvertToSql(null, 0, flags);
+
+									break;
+
+								default:
+									if (level == 0)
+										return Builder.ConvertExpressions(this, expression, flags);
+									break;
+							}
+
+							break;
+						}
+				}
+			}
+
+			throw new InvalidOperationException();
+		}
+
+		SqlInfo[] ConvertMember(MemberInfo member, Expression expression, ConvertFlags flags)
+		{
+			return ConvertExpressions(expression, flags)
+				.Select(si => si.Clone(member))
+				.ToArray();
+		}
+
+		SqlInfo[] ConvertExpressions(Expression expression, ConvertFlags flags)
+		{
+			return Builder.ConvertExpressions(this, expression, flags)
+				.Select(CheckExpression)
+				.ToArray();
+		}
+
+		SqlInfo CheckExpression(SqlInfo expression)
+		{
+			if (expression.Sql is SqlQuery.SearchCondition)
+			{
+				expression.Sql = Builder.Convert(this, new SqlFunction(typeof(bool), "CASE", expression.Sql, new SqlValue(true), new SqlValue(false)));
+			}
+
+			return expression;
+		}
+
+		#endregion
+
+		#region ConvertToIndex
+
+		readonly Dictionary<Tuple<Expression,int,ConvertFlags>,SqlInfo[]> _expressionIndex = new Dictionary<Tuple<Expression,int,ConvertFlags>,SqlInfo[]>();
+
+		public virtual SqlInfo[] ConvertToIndex(Expression expression, int level, ConvertFlags flags)
+		{
+			var key = Tuple.Create(expression, level, flags);
+
+			SqlInfo[] info;
+
+			if (!_expressionIndex.TryGetValue(key, out info))
+			{
+				info = ConvertToIndexInternal(expression, level, flags);
+
+				var newInfo = info
+					.Select(i =>
+					{
+						if (i.Query == SqlQuery)
+							return i;
+
+						return new SqlInfo(i.Members)
+						{
+							Query = SqlQuery,
+							Index = SqlQuery.Select.Add(i.Query.Select.Columns[i.Index])
+						};
+					})
+					.ToArray();
+
+				_expressionIndex.Add(key, newInfo);
+
+				return newInfo;
+			}
+
+			return info;
+		}
+
+		readonly Dictionary<Tuple<MemberInfo,ConvertFlags>,SqlInfo[]> _memberIndex = new Dictionary<Tuple<MemberInfo,ConvertFlags>,SqlInfo[]>();
+
+		SqlInfo[] ConvertToIndexInternal(Expression expression, int level, ConvertFlags flags)
+		{
+			if (IsScalar)
+			{
+				if (Body.NodeType == ExpressionType.Parameter)
+					for (var i = 0; i < Sequence.Length; i++)
+						if (Body == Lambda.Parameters[i])
+							return Sequence[i].ConvertToIndex(expression, level, flags);
+
+				if (expression == null)
+				{
+					var key = Tuple.Create((MemberInfo)null, flags);
+
+					SqlInfo[] idx;
+
+					if (!_memberIndex.TryGetValue(key, out idx))
+					{
+						idx = ConvertToSql(null, 0, flags);
+
+						foreach (var info in idx)
+							SetInfo(info);
+
+						_memberIndex.Add(key, idx);
+					}
+
+					return idx;
+				}
+
+				switch (flags)
+				{
+					case ConvertFlags.Field :
+					case ConvertFlags.All   :
+						return ProcessScalar(
+							expression,
+							level,
+							(ctx, ex, l) => ctx.ConvertToIndex(ex, l, flags),
+							() => GetSequence(expression, level).ConvertToIndex(expression, level + 1, flags));
+				}
+			}
+			else
+			{
+				if (expression == null)
+				{
+					switch (flags)
+					{
+						case ConvertFlags.Field : throw new InvalidOperationException();
+						case ConvertFlags.Key   :
+						case ConvertFlags.All   :
+							{
+								var p = Expression.Parameter(Body.Type, "p");
+								var q =
+									from m in Members.Keys
+									where !(m is MethodInfo)
+									select new
+									{
+										Sql    = ConvertToIndex(Expression.MakeMemberAccess(p, m), 1, flags),
+										Member = m
+									} into mm
+									from m in mm.Sql.Select(s => s.Clone(mm.Member))
+									select m;
+
+								return q.ToArray();
+							}
+					}
+				}
+
+				switch (flags)
+				{
+					case ConvertFlags.All   :
+					case ConvertFlags.Key   :
+					case ConvertFlags.Field :
+						{
+							if (level == 0)
+							{
+								var idx = Builder.ConvertExpressions(this, expression, flags);
+
+								foreach (var info in idx)
+									SetInfo(info);
+
+								return idx;
+							}
+
+							var levelExpression = expression.GetLevelExpression(level);
+
+							switch (levelExpression.NodeType)
+							{
+								case ExpressionType.MemberAccess :
+									{
+										if (levelExpression == expression)
+										{
+											var member = Tuple.Create(((MemberExpression)levelExpression).Member, flags);
+
+											SqlInfo[] idx;
+
+											if (!_memberIndex.TryGetValue(member, out idx))
+											{
+												idx = ConvertToSql(expression, level, flags);
+
+												if (flags == ConvertFlags.Field && idx.Length != 1)
+													throw new InvalidOperationException();
+
+												foreach (var info in idx)
+													SetInfo(info);
+
+												_memberIndex.Add(member, idx);
+											}
+
+											return idx;
+										}
+
+										return ProcessMemberAccess(
+											expression,
+											(MemberExpression)levelExpression,
+											level,
+											(n, ctx, ex, l, _) => n == 0 ?
+												GetSequence(expression, level).ConvertToIndex(expression, level + 1, flags) :
+												ctx.ConvertToIndex(ex, l, flags));
+									}
+
+								case ExpressionType.Parameter:
+
+									if (levelExpression != expression)
+										return GetSequence(expression, level).ConvertToIndex(expression, level + 1, flags);
+									break;
+							}
+
+							break;
+						}
+				}
+			}
+
+			throw new InvalidOperationException();
+		}
+
+		void SetInfo(SqlInfo info)
+		{
+			info.Query = SqlQuery;
+
+			if (info.Sql == SqlQuery)
+				info.Index = SqlQuery.Select.Columns.Count - 1;
+			else
+				info.Index = SqlQuery.Select.Add(info.Sql);
+		}
+
+		#endregion
+
+		#region IsExpression
+
+		public virtual IsExpressionResult IsExpression(Expression expression, int level, RequestFor requestFlag)
+		{
+			switch (requestFlag)
+			{
+				case RequestFor.SubQuery : return IsExpressionResult.False;
+				case RequestFor.Root     :
+					return new IsExpressionResult(Sequence.Length == 1 ?
+						expression == Lambda.Parameters[0] :
+						Lambda.Parameters.Any(p => p == expression));
+			}
+
+			if (IsScalar)
+			{
+				if (expression == null)
+					return IsExpression(Body, 0, requestFlag);
+
+				switch (requestFlag)
+				{
+					default                     : return IsExpressionResult.False;
+					case RequestFor.Table       :
+					case RequestFor.Association :
+					case RequestFor.Field       :
+					case RequestFor.Expression  :
+					case RequestFor.Object      :
+					case RequestFor.GroupJoin   :
+						return ProcessScalar(
+							expression,
+							level,
+							(ctx, ex, l) => ctx.IsExpression(ex, l, requestFlag),
+							() => new IsExpressionResult(requestFlag == RequestFor.Expression));
+				}
+			}
+			else
+			{
+				switch (requestFlag)
+				{
+					default                     : return IsExpressionResult.False;
+					case RequestFor.Table       :
+					case RequestFor.Association :
+					case RequestFor.Field       :
+					case RequestFor.Expression  :
+					case RequestFor.Object      :
+					case RequestFor.GroupJoin   :
+						{
+							if (expression == null)
+							{
+								if (requestFlag == RequestFor.Expression)
+									return new IsExpressionResult(Members.Values.Any(member => IsExpression(member, 0, requestFlag).Result));
+
+								return new IsExpressionResult(requestFlag == RequestFor.Object);
+							}
+
+							var levelExpression = expression.GetLevelExpression(level);
+
+							switch (levelExpression.NodeType)
+							{
+								case ExpressionType.MemberAccess :
+									{
+										var member = ((MemberExpression)levelExpression).Member;
+
+										Expression memberExpression;
+
+										if (!Members.TryGetValue(member, out memberExpression))
+										{
+											var nm = Members.Keys.FirstOrDefault(m => m.Name == member.Name);
+
+											if (nm != null && member.DeclaringType.IsInterface)
+											{
+												if (TypeHelper.IsSameOrParent(member.DeclaringType, nm.DeclaringType))
+													memberExpression = Members[nm];
+												else
+												{
+													var mdt = TypeHelper.GetDefiningTypes(member.DeclaringType, member);
+													var ndt = TypeHelper.GetDefiningTypes(Body.Type,            nm);
+
+													if (mdt.Intersect(ndt).Any())
+														memberExpression = Members[nm];
+												}
+											}
+
+											if (memberExpression == null)
+												return new IsExpressionResult(requestFlag == RequestFor.Expression);
+												//throw new InvalidOperationException(
+												//	string.Format("Invalid member '{0}.{1}'", member.DeclaringType, member.Name));
+										}
+
+										if (levelExpression == expression)
+										{
+											switch (memberExpression.NodeType)
+											{
+												case ExpressionType.New        :
+												case ExpressionType.MemberInit :
+													return new IsExpressionResult(requestFlag == RequestFor.Object);
+											}
+										}
+
+										return ProcessMemberAccess(
+											expression,
+											(MemberExpression)levelExpression,
+											level,
+											(n,ctx,ex,l,_) => n == 0 ?
+												new IsExpressionResult(requestFlag == RequestFor.Expression) : 
+												ctx.IsExpression(ex, l, requestFlag));
+									}
+
+								case ExpressionType.Parameter    :
+									{
+										var sequence  = GetSequence(expression, level);
+										var parameter = Lambda.Parameters[Sequence.Length == 0 ? 0 : Array.IndexOf(Sequence, sequence)];
+
+										if (levelExpression == expression)
+										{
+											if (levelExpression == parameter)
+												return sequence.IsExpression(null, 0, requestFlag);
+										}
+										else if (level == 0)
+											return sequence.IsExpression(expression, 1, requestFlag);
+
+										break;
+									}
+
+								case ExpressionType.New          :
+								case ExpressionType.MemberInit   : return new IsExpressionResult(requestFlag == RequestFor.Object);
+								default                          : return new IsExpressionResult(requestFlag == RequestFor.Expression);
+							}
+
+							break;
+						}
+				}
+			}
+
+			throw new InvalidOperationException();
+		}
+
+		#endregion
+
+		#region GetContext
+
+		public virtual IBuildContext GetContext(Expression expression, int level, BuildInfo buildInfo)
+		{
+			if (expression == null)
+				return this;
+
+			if (IsScalar)
+			{
+				return ProcessScalar(
+					expression,
+					level,
+					(ctx, ex, l) => ctx.GetContext(ex, l, buildInfo),
+					() => { throw new InvalidOperationException(); });
+			}
+			else
+			{
+				var levelExpression = expression.GetLevelExpression(level);
+
+				switch (levelExpression.NodeType)
+				{
+					case ExpressionType.MemberAccess :
+						{
+							if (levelExpression == expression && Sequence.Length == 1 && !(Sequence[0] is GroupByBuilder.GroupByContext))
+							{
+								var memberExpression = GetMemberExpression(
+									((MemberExpression)levelExpression).Member,
+									levelExpression == expression,
+									levelExpression.Type);
+
+								//var sequence = GetSequence(memberExpression, 0);
+								//return sequence.GetContext(memberExpression, 1, new BuildInfo(buildInfo, memberExpression));
+
+								var ctx = GetContext(memberExpression, 0, new BuildInfo(this, memberExpression, buildInfo.SqlQuery));
+
+								if (ctx != null)
+								{
+									return ctx;
+								}
+							}
+
+							var context = ProcessMemberAccess(
+								expression,
+								(MemberExpression)levelExpression,
+								level,
+								(n,ctx,ex,l,_) => n == 0 ?
+									null :
+									ctx.GetContext(ex, l, buildInfo));
+
+							if (context == null)
+								throw new InvalidOperationException();
+
+							return context;
+						}
+
+					case ExpressionType.Parameter    :
+						{
+							var sequence  = GetSequence(expression, level);
+							var parameter = Lambda.Parameters[Sequence.Length == 0 ? 0 : Array.IndexOf(Sequence, sequence)];
+
+							if (levelExpression == expression)
+							{
+								if (levelExpression == parameter)
+									return sequence.GetContext(null, 0, buildInfo);
+							}
+							else if (level == 0)
+								return sequence.GetContext(expression, 1, buildInfo);
+
+							break;
+						}
+				}
+
+				if (level == 0)
+				{
+					var sequence = GetSequence(expression, level);
+					return sequence.GetContext(expression, level + 1, buildInfo);
+				}
+			}
+
+			throw new InvalidOperationException();
+		}
+
+		#endregion
+
+		#region ConvertToParentIndex
+
+		public virtual int ConvertToParentIndex(int index, IBuildContext context)
+		{
+			if (context.SqlQuery != SqlQuery)
+				index = SqlQuery.Select.Add(context.SqlQuery.Select.Columns[index]);
+
+			return Parent == null ? index : Parent.ConvertToParentIndex(index, this);
+		}
+
+		#endregion
+
+		#region SetAlias
+
+		public virtual void SetAlias(string alias)
+		{
+		}
+
+		#endregion
+
+		#region GetSubQuery
+
+		public ISqlExpression GetSubQuery(IBuildContext context)
+		{
+			return null;
+		}
+
+		#endregion
+
+		#region Helpers
+
+		T ProcessScalar<T>(Expression expression, int level, Func<IBuildContext,Expression,int,T> action, Func<T> defaultAction)
+		{
+			if (level == 0)
+			{
+				if (Body.NodeType == ExpressionType.Parameter)
+				{
+					var sequence = GetSequence(Body, 0);
+
+					return expression == Body ?
+						action(sequence, null,       0) :
+						action(sequence, expression, 1);
+				}
+
+				var levelExpression = expression.GetLevelExpression(level);
+
+				if (levelExpression != expression)
+				{
+					var ctx = GetSequence(expression, level);
+					return ctx == null ? defaultAction() : action(ctx, expression, Sequence.Contains(ctx) ? level + 1 : 0);
+				}
+
+				if (expression.NodeType == ExpressionType.Parameter)
+				{
+					var sequence  = GetSequence(expression, level);
+					var parameter = Lambda.Parameters[Sequence.Length == 0 ? 0 : Array.IndexOf(Sequence, sequence)];
+
+					if (levelExpression == parameter)
+						return action(sequence, null, 0);
+				}
+
+				switch (Body.NodeType)
+				{
+					case ExpressionType.MemberAccess : return action(GetSequence(expression, level), null, 0);
+					default                          : return defaultAction();
+				}
+			}
+			else
+			{
+				var root = Body.GetRootObject();
+
+				if (root.NodeType == ExpressionType.Parameter)
+				{
+					var levelExpression = expression.GetLevelExpression(level - 1);
+					var newExpression   = GetExpression(expression, levelExpression, Body);
+
+					return action(this, newExpression, 0);
+				}
+			}
+
+			throw new InvalidOperationException();
+		}
+
+		T ProcessMemberAccess<T>(Expression expression, MemberExpression levelExpression, int level,
+			Func<int,IBuildContext,Expression,int,Expression,T> action)
+		{
+			var memberExpression = Members[levelExpression.Member];
+			var newExpression    = GetExpression(expression, levelExpression, memberExpression);
+			var sequence         = GetSequence  (expression, level);
+			var nextLevel        = 1;
+
+			if (sequence != null)
+			{
+				var idx = Sequence.Length == 0 ? 0 : Array.IndexOf(Sequence, sequence);
+
+				if (idx >= 0)
+				{
+					var parameter = Lambda.Parameters[idx];
+
+					if (levelExpression == expression)
+					{
+						if (memberExpression == parameter)
+							return action(1, sequence, null, 0, memberExpression);
+
+//						if (!(sequence is GroupByBuilder.GroupByContext) && memberExpression.GetRootObject() == parameter)
+//							return action(3, this, newExpression, 0, memberExpression);
+					}
+				}
+				else
+				{
+					nextLevel = 0;
+				}
+			}
+
+			switch (memberExpression.NodeType)
+			{
+				case ExpressionType.MemberAccess :
+				case ExpressionType.Parameter    :
+					if (sequence != null)
+						return action(2, sequence, newExpression, nextLevel, memberExpression);
+					throw new InvalidOperationException();
+
+				case ExpressionType.New          :
+				case ExpressionType.MemberInit   :
+					{
+						var mmExpresion = GetMemberExpression(memberExpression, expression, level + 1);
+						return action(3, this, mmExpresion, 0, memberExpression);
+					}
+			}
+
+			return action(0, this, null, 0, memberExpression);
+		}
+
+		protected bool IsSubQuery()
+		{
+			for (var p = Parent; p != null; p = p.Parent)
+				if (p.IsExpression(null, 0, RequestFor.SubQuery).Result)
+					return true;
+			return false;
+		}
+
+		IBuildContext GetSequence(Expression expression, int level)
+		{
+			if (Sequence.Length == 1 && Sequence[0].Parent == null)
+				return Sequence[0];
+
+			Expression root = null;
+
+			if (IsScalar)
+			{
+				root = expression.GetRootObject();
+			}
+			else
+			{
+				var levelExpression = expression.GetLevelExpression(level);
+
+				switch (levelExpression.NodeType)
+				{
+					case ExpressionType.MemberAccess :
+						{
+							var memberExpression = Members[((MemberExpression)levelExpression).Member];
+
+							root =  memberExpression.GetRootObject();
+
+							if (root.NodeType != ExpressionType.Parameter)
+								return null;
+
+							break;
+						}
+
+					case ExpressionType.Parameter :
+						{
+							root = expression.GetRootObject();
+							break;
+						}
+				}
+			}
+
+			if (root != null)
+				for (var i = 0; i < Lambda.Parameters.Count; i++)
+					if (root == Lambda.Parameters[i])
+						return Sequence[i];
+
+			foreach (var context in Sequence)
+			{
+				if (context.Parent != null)
+				{
+					var ctx = Builder.GetContext(context, root);
+					if (ctx != null)
+						return ctx;
+				}
+			}
+
+			return null;
+		}
+
+		static Expression GetExpression(Expression expression, Expression levelExpression, Expression memberExpression)
+		{
+			return levelExpression != expression ?
+				expression.Convert(ex => ex == levelExpression ? memberExpression : ex) :
+				memberExpression;
+		}
+
+		static Expression GetMemberExpression(Expression newExpression, Expression expression, int level)
+		{
+			var levelExpresion = expression.GetLevelExpression(level);
+
+			switch (newExpression.NodeType)
+			{
+				case ExpressionType.New        :
+				case ExpressionType.MemberInit : break;
+				default                        :
+					var le = expression.GetLevelExpression(level - 1);
+					return GetExpression(expression, le, newExpression);
+			}
+
+			if (levelExpresion.NodeType != ExpressionType.MemberAccess)
+				throw new LinqException("Invalid expression {0}", levelExpresion);
+
+			var me = (MemberExpression)levelExpresion;
+
+			switch (newExpression.NodeType)
+			{
+				case ExpressionType.New:
+					{
+						var expr = (NewExpression)newExpression;
+
+// ReSharper disable ConditionIsAlwaysTrueOrFalse
+// ReSharper disable HeuristicUnreachableCode
+						if (expr.Members == null)
+							throw new LinqException("Invalid expression {0}", expression);
+// ReSharper restore HeuristicUnreachableCode
+// ReSharper restore ConditionIsAlwaysTrueOrFalse
+
+						for (var i = 0; i < expr.Members.Count; i++)
+							if (me.Member == expr.Members[i])
+								return levelExpresion == expression ?
+									expr.Arguments[i].Unwrap() :
+									GetMemberExpression(expr.Arguments[i].Unwrap(), expression, level + 1);
+
+						throw new LinqException("Invalid expression {0}", expression);
+					}
+
+				case ExpressionType.MemberInit:
+					{
+						var expr = (MemberInitExpression)newExpression;
+
+						foreach (var binding in expr.Bindings.Cast<MemberAssignment>())
+						{
+							if (me.Member == binding.Member)
+								return levelExpresion == expression ?
+									binding.Expression.Unwrap() :
+									GetMemberExpression(binding.Expression.Unwrap(), expression, level + 1);
+						}
+
+						throw new LinqException("Invalid expression {0}", expression);
+					}
+			}
+
+			return expression;
+		}
+
+		Expression GetMemberExpression(MemberInfo member, bool add, Type type)
+		{
+			Expression memberExpression;
+
+			if (!Members.TryGetValue(member, out memberExpression))
+			{
+				foreach (var m in Members)
+				{
+					if (m.Key.Name == member.Name)
+					{
+						if (TypeHelper.Equals(m.Key, member, IsScalar ? null : Body.Type))
+							return m.Value;
+					}
+				}
+
+				if (add && TypeHelper.IsSameOrParent(member.DeclaringType, Body.Type))
+				{
+					memberExpression = Expression.Constant(
+						TypeHelper.GetDefaultValue(type), type);
+
+					Members.Add(member, memberExpression);
+				}
+				else
+					throw new InvalidOperationException();
+			}
+
+			return memberExpression;
+		}
+
+		#endregion
+	}
+}