Mercurial > pub > bltoolkit
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 + } +}