Mercurial > pub > bltoolkit
diff Source/Data/Linq/Builder/SubQueryContext.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/SubQueryContext.cs Thu Mar 27 21:46:09 2014 +0400 @@ -0,0 +1,171 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +namespace BLToolkit.Data.Linq.Builder +{ + using BLToolkit.Linq; + using Data.Sql; + + class SubQueryContext : PassThroughContext + { + public readonly IBuildContext SubQuery; + + public SubQueryContext(IBuildContext subQuery, SqlQuery sqlQuery, bool addToSql) + : base(subQuery) + { + if (sqlQuery == subQuery.SqlQuery) + throw new ArgumentException("Wrong subQuery argument.", "subQuery"); + + SubQuery = subQuery; + SubQuery.Parent = this; + + SqlQuery = sqlQuery; + + if (addToSql) + sqlQuery.From.Table(SubQuery.SqlQuery); + } + + public SubQueryContext(IBuildContext subQuery, bool addToSql) + : this(subQuery, new SqlQuery { ParentSql = subQuery.SqlQuery.ParentSql }, addToSql) + { + } + + public SubQueryContext(IBuildContext subQuery) + : this(subQuery, true) + { + } + + public override SqlQuery SqlQuery { get; set; } + public override IBuildContext Parent { get; set; } + + public override void BuildQuery<T>(Query<T> query, ParameterExpression queryParameter) + { + if (Expression.NodeType == ExpressionType.Lambda) + { + var le = (LambdaExpression)Expression; + + if (le.Parameters.Count == 1 && null != Expression.Find( + e => e.NodeType == ExpressionType.Call && ((MethodCallExpression)e).IsQueryable())) + { + if (le.Body.NodeType == ExpressionType.New) + { + var ne = (NewExpression)le.Body; + var p = Expression.Parameter(ne.Type, "p"); + + var seq = new SelectContext( + Parent, + Expression.Lambda( + Expression.New( + ne.Constructor, + ne.Members.Select(m => Expression.MakeMemberAccess(p, m)).ToArray(), + ne.Members), + p), + this); + + seq.BuildQuery(query, queryParameter); + + return; + } + + if (le.Body.NodeType == ExpressionType.MemberInit) + { + var mi = (MemberInitExpression)le.Body; + + if (mi.NewExpression.Arguments.Count == 0 && mi.Bindings.All(b => b is MemberAssignment)) + { + var p = Expression.Parameter(mi.Type, "p"); + + var seq = new SelectContext( + Parent, + Expression.Lambda( + Expression.MemberInit( + mi.NewExpression, + mi.Bindings + .OfType<MemberAssignment>() + .Select(ma => Expression.Bind(ma.Member, Expression.MakeMemberAccess(p, ma.Member))) + .ToArray()), + p), + this); + + seq.BuildQuery(query, queryParameter); + + return; + } + } + } + } + + base.BuildQuery(query, queryParameter); + } + + public override SqlInfo[] ConvertToSql(Expression expression, int level, ConvertFlags flags) + { + return SubQuery + .ConvertToIndex(expression, level, flags) + .Select(idx => new SqlInfo(idx.Members) { Sql = SubQuery.SqlQuery.Select.Columns[idx.Index] }) + .ToArray(); + } + + // JoinContext has similar logic. Consider to review it. + // + public override SqlInfo[] ConvertToIndex(Expression expression, int level, ConvertFlags flags) + { + return ConvertToSql(expression, level, flags) + .Select(idx => + { + idx.Query = SqlQuery; + idx.Index = GetIndex((SqlQuery.Column)idx.Sql); + + return idx; + }) + .ToArray(); + } + + public override IsExpressionResult IsExpression(Expression expression, int level, RequestFor testFlag) + { + switch (testFlag) + { + case RequestFor.SubQuery : return IsExpressionResult.True; + } + + return base.IsExpression(expression, level, testFlag); + } + + internal protected readonly Dictionary<ISqlExpression,int> ColumnIndexes = new Dictionary<ISqlExpression,int>(); + + protected virtual int GetIndex(SqlQuery.Column column) + { + int idx; + + if (!ColumnIndexes.TryGetValue(column, out idx)) + { + idx = SqlQuery.Select.Add(column); + ColumnIndexes.Add(column, idx); + } + + return idx; + } + + public override int ConvertToParentIndex(int index, IBuildContext context) + { + var idx = GetIndex(context.SqlQuery.Select.Columns[index]); + return Parent == null ? idx : Parent.ConvertToParentIndex(idx, this); + } + + public override void SetAlias(string alias) + { + if (alias.Contains('<')) + return; + + if (SqlQuery.From.Tables[0].Alias == null) + SqlQuery.From.Tables[0].Alias = alias; + } + + public override ISqlExpression GetSubQuery(IBuildContext context) + { + return null; + } + } +}