diff Source/Data/Linq/CompiledQuery.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/CompiledQuery.cs	Thu Mar 27 21:46:09 2014 +0400
@@ -0,0 +1,336 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+
+namespace BLToolkit.Data.Linq
+{
+	using BLToolkit.Linq;
+	using Reflection;
+
+	/// <summary>
+	/// Provides for compilation and caching of queries for reuse.
+	/// </summary>
+	public class CompiledQuery
+	{
+		protected CompiledQuery(LambdaExpression query)
+		{
+			_query = query;
+		}
+
+		readonly object                _sync = new object();
+		readonly LambdaExpression      _query;
+		volatile Func<object[],object> _compiledQuery;
+
+		TResult ExecuteQuery<TResult>(params object[] args)
+		{
+			if (_compiledQuery == null)
+				lock (_sync)
+					if (_compiledQuery == null)
+						_compiledQuery = CompileQuery(_query);
+
+			return (TResult)_compiledQuery(args);
+		}
+
+		private interface ITableHelper
+		{
+			Expression CallTable(LambdaExpression query, Expression expr, ParameterExpression ps, bool isQueriable);
+		}
+
+		internal class TableHelper<T> : ITableHelper
+		{
+			public Expression CallTable(LambdaExpression query, Expression expr, ParameterExpression ps, bool isQueriable)
+			{
+				var table = new CompiledTable<T>(query, expr);
+
+				return Expression.Call(
+					Expression.Constant(table),
+					isQueriable ?
+						ReflectionHelper.Expressor<CompiledTable<T>>.MethodExpressor(t => t.Create (null)) :
+						ReflectionHelper.Expressor<CompiledTable<T>>.MethodExpressor(t => t.Execute(null)),
+					ps);
+			}
+		}
+
+		static Func<object[],object> CompileQuery(LambdaExpression query)
+		{
+			var ps = Expression.Parameter(typeof(object[]), "ps");
+
+			var info = query.Body.Convert(pi =>
+			{
+				switch (pi.NodeType)
+				{
+					case ExpressionType.Parameter :
+						{
+							var idx = query.Parameters.IndexOf((ParameterExpression)pi);
+
+							if (idx >= 0)
+								return Expression.Convert(Expression.ArrayIndex(ps, Expression.Constant(idx)), pi.Type);
+
+							break;
+						}
+
+					case ExpressionType.Call :
+						{
+							var expr = (MethodCallExpression)pi;
+
+							if (expr.IsQueryable())
+							{
+								var qtype  = TypeHelper.GetGenericType(
+									TypeHelper.IsSameOrParent(typeof(IQueryable), expr.Type) ?
+										typeof(IQueryable<>) :
+										typeof(IEnumerable<>),
+									expr.Type);
+
+								var helper = (ITableHelper)Activator.CreateInstance(
+									typeof(TableHelper<>).MakeGenericType(qtype == null ? expr.Type : qtype.GetGenericArguments()[0]));
+
+								return helper.CallTable(query, expr, ps, qtype != null);
+							}
+
+							if (expr.Method.Name == "GetTable" && expr.Method.DeclaringType == typeof(Extensions))
+								goto case ExpressionType.MemberAccess;
+						}
+
+						break;
+
+					case ExpressionType.MemberAccess :
+						if (pi.Type.IsGenericType && pi.Type.GetGenericTypeDefinition() == typeof(Table<>))
+						{
+							var helper = (ITableHelper)Activator
+								.CreateInstance(typeof(TableHelper<>)
+								.MakeGenericType(pi.Type.GetGenericArguments()[0]));
+							return helper.CallTable(query, pi, ps, true);
+						}
+
+						break;
+				}
+
+				return pi;
+			});
+
+			return Expression.Lambda<Func<object[],object>>(Expression.Convert(info, typeof(object)), ps).Compile();
+		}
+
+		#region Invoke
+
+		public TResult Invoke<TDC,TResult>(TDC dataContext)
+		{
+			return ExecuteQuery<TResult>(dataContext);
+		}
+
+		public TResult Invoke<TDC,T1,TResult>(TDC dataContext, T1 arg1)
+		{
+			return ExecuteQuery<TResult>(dataContext, arg1);
+		}
+
+		public TResult Invoke<TDC,T1,T2,TResult>(TDC dataContext, T1 arg1, T2 arg2)
+		{
+			return ExecuteQuery<TResult>(dataContext, arg1, arg2);
+		}
+
+		public TResult Invoke<TDC,T1,T2,T3,TResult>(TDC dataContext, T1 arg1, T2 arg2, T3 arg3)
+		{
+			return ExecuteQuery<TResult>(dataContext, arg1, arg2, arg3);
+		}
+
+		public TResult Invoke<TDC,T1,T2,T3,T4,TResult>(TDC dataContext, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
+		{
+			return ExecuteQuery<TResult>(dataContext, arg1, arg2, arg3, arg4);
+		}
+
+		public TResult Invoke<TDC,T1,T2,T3,T4,T5,TResult>(TDC dataContext, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
+		{
+			return ExecuteQuery<TResult>(dataContext, arg1, arg2, arg3, arg4, arg5);
+		}
+
+		#endregion
+
+		#region Compile
+
+		/// <summary>
+		/// Compiles the query.
+		/// </summary>
+		/// <returns>
+		/// A generic delegate that represents the compiled query.
+		/// </returns>
+		/// <param name="query">
+		/// The query expression to be compiled.
+		/// </param>
+		/// <typeparam name="TDC ">
+		/// Represents the type of the parameter that has to be passed in when executing the delegate returned by the method.
+		/// </typeparam>
+		/// <typeparam name="TResult">
+		/// Returned type of the delegate returned by the method.
+		/// </typeparam>
+		public static Func<TDC,TResult> Compile<TDC,TResult>(
+			[JetBrains.Annotations.NotNull] Expression<Func<TDC,TResult>> query)
+			  where TDC : IDataContext
+		{
+			if (query == null) throw new ArgumentNullException("query");
+			return new CompiledQuery(query).Invoke<TDC,TResult>;
+		}
+
+		/// <summary>
+		/// Compiles the query.
+		/// </summary>
+		/// <returns>
+		/// A generic delegate that represents the compiled query.
+		/// </returns>
+		/// <param name="query">
+		/// The query expression to be compiled.
+		/// </param>
+		/// <typeparam name="TDC">
+		/// Represents the type of the parameter that has to be passed in when executing the delegate returned by the method.
+		/// </typeparam>
+		/// <typeparam name="TArg1">
+		/// Represents the type of the parameter that has to be passed in when executing the delegate returned by the method.
+		/// </typeparam>
+		/// <typeparam name="TResult">
+		/// Returned type of the delegate returned by the method.
+		/// </typeparam>
+		public static Func<TDC,TArg1,TResult> Compile<TDC,TArg1,TResult>(
+			[JetBrains.Annotations.NotNull] Expression<Func<TDC,TArg1,TResult>> query)
+			where TDC : IDataContext
+		{
+			if (query == null) throw new ArgumentNullException("query");
+			return new CompiledQuery(query).Invoke<TDC,TArg1,TResult>;
+		}
+
+		/// <summary>
+		/// Compiles the query.
+		/// </summary>
+		/// <returns>
+		/// A generic delegate that represents the compiled query.
+		/// </returns>
+		/// <param name="query">
+		/// The query expression to be compiled.
+		/// </param>
+		/// <typeparam name="TDC ">
+		/// Represents the type of the parameter that has to be passed in when executing the delegate returned by the method.
+		/// </typeparam>
+		/// <typeparam name="TArg1">
+		/// Represents the type of the parameter that has to be passed in when executing the delegate returned by the method.
+		/// </typeparam>
+		/// <typeparam name="TArg2">
+		/// Represents the type of the parameter that has to be passed in when executing the delegate returned by the method.
+		/// </typeparam>
+		/// <typeparam name="TResult">
+		/// Returned type of the delegate returned by the method.
+		/// </typeparam>
+		public static Func<TDC,TArg1,TArg2,TResult> Compile<TDC,TArg1,TArg2,TResult>(
+			[JetBrains.Annotations.NotNull] Expression<Func<TDC,TArg1,TArg2,TResult>> query)
+			where TDC : IDataContext
+		{
+			if (query == null) throw new ArgumentNullException("query");
+			return new CompiledQuery(query).Invoke<TDC,TArg1,TArg2,TResult>;
+		}
+
+		/// <summary>
+		/// Compiles the query.
+		/// </summary>
+		/// <returns>
+		/// A generic delegate that represents the compiled query.
+		/// </returns>
+		/// <param name="query">
+		/// The query expression to be compiled.
+		/// </param>
+		/// <typeparam name="TDC ">
+		/// Represents the type of the parameter that has to be passed in when executing the delegate returned by the method.
+		/// </typeparam>
+		/// <typeparam name="TArg1">
+		/// Represents the type of the parameter that has to be passed in when executing the delegate returned by the method.
+		/// </typeparam>
+		/// <typeparam name="TArg2">
+		/// Represents the type of the parameter that has to be passed in when executing the delegate returned by the method.
+		/// </typeparam>
+		/// <typeparam name="TArg3">
+		/// Represents the type of the parameter that has to be passed in when executing the delegate returned by the method.
+		/// </typeparam>
+		/// <typeparam name="TResult">
+		/// Returned type of the delegate returned by the method.
+		/// </typeparam>
+		public static Func<TDC,TArg1,TArg2,TArg3,TResult> Compile<TDC,TArg1,TArg2,TArg3,TResult>(
+			[JetBrains.Annotations.NotNull] Expression<Func<TDC,TArg1,TArg2,TArg3,TResult>> query)
+			where TDC : IDataContext
+		{
+			if (query == null) throw new ArgumentNullException("query");
+			return new CompiledQuery(query).Invoke<TDC,TArg1,TArg2,TArg3,TResult>;
+		}
+
+		/// <summary>
+		/// Compiles the query.
+		/// </summary>
+		/// <returns>
+		/// A generic delegate that represents the compiled query.
+		/// </returns>
+		/// <param name="query">
+		/// The query expression to be compiled.
+		/// </param>
+		/// <typeparam name="TDC ">
+		/// Represents the type of the parameter that has to be passed in when executing the delegate returned by the method.
+		/// </typeparam>
+		/// <typeparam name="TArg1">
+		/// Represents the type of the parameter that has to be passed in when executing the delegate returned by the method.
+		/// </typeparam>
+		/// <typeparam name="TArg2">
+		/// Represents the type of the parameter that has to be passed in when executing the delegate returned by the method.
+		/// </typeparam>
+		/// <typeparam name="TArg3">
+		/// Represents the type of the parameter that has to be passed in when executing the delegate returned by the method.
+		/// </typeparam>
+		/// <typeparam name="TArg4">
+		/// Represents the type of the parameter that has to be passed in when executing the delegate returned by the method.
+		/// </typeparam>
+		/// <typeparam name="TResult">
+		/// Returned type of the delegate returned by the method.
+		/// </typeparam>
+		public static Func<TDC,TArg1,TArg2,TArg3,TArg4,TResult> Compile<TDC,TArg1,TArg2,TArg3,TArg4,TResult>(
+			[JetBrains.Annotations.NotNull] Expression<Func<TDC,TArg1,TArg2,TArg3,TArg4,TResult>> query)
+			where TDC : IDataContext
+		{
+			if (query == null) throw new ArgumentNullException("query");
+			return new CompiledQuery(query).Invoke<TDC,TArg1,TArg2,TArg3,TArg4,TResult>;
+		}
+
+		/// <summary>
+		/// Compiles the query.
+		/// </summary>
+		/// <returns>
+		/// A generic delegate that represents the compiled query.
+		/// </returns>
+		/// <param name="query">
+		/// The query expression to be compiled.
+		/// </param>
+		/// <typeparam name="TDC ">
+		/// Represents the type of the parameter that has to be passed in when executing the delegate returned by the method.
+		/// </typeparam>
+		/// <typeparam name="TArg1">
+		/// Represents the type of the parameter that has to be passed in when executing the delegate returned by the method.
+		/// </typeparam>
+		/// <typeparam name="TArg2">
+		/// Represents the type of the parameter that has to be passed in when executing the delegate returned by the method.
+		/// </typeparam>
+		/// <typeparam name="TArg3">
+		/// Represents the type of the parameter that has to be passed in when executing the delegate returned by the method.
+		/// </typeparam>
+		/// <typeparam name="TArg4">
+		/// Represents the type of the parameter that has to be passed in when executing the delegate returned by the method.
+		/// </typeparam>
+		/// <typeparam name="TArg5">
+		/// Represents the type of the parameter that has to be passed in when executing the delegate returned by the method.
+		/// </typeparam>
+		/// <typeparam name="TResult">
+		/// Returned type of the delegate returned by the method.
+		/// </typeparam>
+		public static Func<TDC,TArg1,TArg2,TArg3,TArg4,TArg5,TResult> Compile<TDC,TArg1,TArg2,TArg3,TArg4,TArg5,TResult>(
+			[JetBrains.Annotations.NotNull] Expression<Func<TDC,TArg1,TArg2,TArg3,TArg4,TArg5,TResult>> query)
+			where TDC : IDataContext
+		{
+			if (query == null) throw new ArgumentNullException("query");
+			return new CompiledQuery(query).Invoke<TDC,TArg1,TArg2,TArg3,TArg4,TArg5,TResult>;
+		}
+
+		#endregion
+	}
+}