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;
+		}
+	}
+}