You've already forked linux-packaging-mono
							
							
		
			
				
	
	
		
			750 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			750 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| //---------------------------------------------------------------------
 | |
| // <copyright file="Funcletizer.cs" company="Microsoft">
 | |
| //      Copyright (c) Microsoft Corporation.  All rights reserved.
 | |
| // </copyright>
 | |
| //
 | |
| // @owner       Microsoft
 | |
| // @backupOwner Microsoft
 | |
| //---------------------------------------------------------------------
 | |
| 
 | |
| namespace System.Data.Objects.ELinq
 | |
| {
 | |
|     using System.Collections.Generic;
 | |
|     using System.Collections.ObjectModel;
 | |
|     using System.Data.Common.CommandTrees;
 | |
|     using System.Data.Common.CommandTrees.ExpressionBuilder;
 | |
|     using System.Data.Entity;
 | |
|     using System.Data.Metadata.Edm;
 | |
|     using System.Diagnostics;
 | |
|     using System.Globalization;
 | |
|     using System.Linq;
 | |
|     using System.Linq.Expressions;
 | |
|     using System.Reflection;
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Determines which leaves of a LINQ expression tree should be evaluated locally before
 | |
|     /// sending a query to the store. These sub-expressions may map to query parameters (e.g. local variables),
 | |
|     /// to constants (e.g. literals 'new DateTime(2008, 1, 1)') or query sub-expression
 | |
|     /// (e.g. 'context.Products'). Parameter expressions are replaced with QueryParameterExpression
 | |
|     /// nodes. All other elements are swapped in place with either expanded expressions (for sub-queries)
 | |
|     /// or constants. Where the expression includes mutable state that may influence the translation
 | |
|     /// to a query, a Func(Of Boolean) delegate is returned indicating when a recompilation is necessary.
 | |
|     /// </summary>
 | |
|     internal sealed class Funcletizer
 | |
|     {
 | |
|         // Compiled query information
 | |
|         private readonly ParameterExpression _rootContextParameter;
 | |
|         private readonly ObjectContext _rootContext;
 | |
|         private readonly ConstantExpression _rootContextExpression;
 | |
|         private readonly ReadOnlyCollection<ParameterExpression> _compiledQueryParameters;
 | |
|         private readonly Mode _mode;
 | |
|         private readonly HashSet<Expression> _linqExpressionStack = new HashSet<Expression>();
 | |
| 
 | |
|         // Object parameters
 | |
|         private static readonly string s_parameterPrefix = "p__linq__";
 | |
|         private long _parameterNumber;
 | |
| 
 | |
|         private Funcletizer(
 | |
|             Mode mode,
 | |
|             ObjectContext rootContext, 
 | |
|             ParameterExpression rootContextParameter, 
 | |
|             ReadOnlyCollection<ParameterExpression> compiledQueryParameters)
 | |
|         {
 | |
|             _mode = mode;
 | |
|             _rootContext = rootContext;
 | |
|             _rootContextParameter = rootContextParameter;
 | |
|             _compiledQueryParameters = compiledQueryParameters;
 | |
|             if (null != _rootContextParameter && null != _rootContext)
 | |
|             {
 | |
|                 _rootContextExpression = Expression.Constant(_rootContext);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         internal static Funcletizer CreateCompiledQueryEvaluationFuncletizer(
 | |
|             ObjectContext rootContext,
 | |
|             ParameterExpression rootContextParameter,
 | |
|             ReadOnlyCollection<ParameterExpression> compiledQueryParameters)
 | |
|         {
 | |
|             EntityUtil.CheckArgumentNull(rootContext, "rootContext");
 | |
|             EntityUtil.CheckArgumentNull(rootContextParameter, "rootContextParameter");
 | |
|             EntityUtil.CheckArgumentNull(compiledQueryParameters, "compiledQueryParameters");
 | |
| 
 | |
|             return new Funcletizer(Mode.CompiledQueryEvaluation, rootContext, rootContextParameter, compiledQueryParameters);
 | |
|         }
 | |
| 
 | |
|         internal static Funcletizer CreateCompiledQueryLockdownFuncletizer()
 | |
|         {
 | |
|             return new Funcletizer(Mode.CompiledQueryLockdown, null, null, null);
 | |
|         }
 | |
| 
 | |
|         internal static Funcletizer CreateQueryFuncletizer(ObjectContext rootContext)
 | |
|         {
 | |
|             EntityUtil.CheckArgumentNull(rootContext, "rootContext");
 | |
| 
 | |
|             return new Funcletizer(Mode.ConventionalQuery, rootContext, null, null);
 | |
|         }
 | |
| 
 | |
|         internal ObjectContext RootContext
 | |
|         {
 | |
|             get { return _rootContext; }
 | |
|         }
 | |
| 
 | |
|         internal ParameterExpression RootContextParameter
 | |
|         {
 | |
|             get { return _rootContextParameter; }
 | |
|         }
 | |
| 
 | |
|         internal ConstantExpression RootContextExpression
 | |
|         {
 | |
|             get { return _rootContextExpression; }
 | |
|         }
 | |
| 
 | |
|         internal bool IsCompiledQuery
 | |
|         {
 | |
|             get { return _mode == Mode.CompiledQueryEvaluation || _mode == Mode.CompiledQueryLockdown; }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Performs funcletization on the given expression. Also returns a delegates that can be used
 | |
|         /// to determine if the entire tree needs to be recompiled.
 | |
|         /// </summary>
 | |
|         internal Expression Funcletize(Expression expression, out Func<bool> recompileRequired)
 | |
|         {
 | |
|             EntityUtil.CheckArgumentNull(expression, "expression");
 | |
| 
 | |
|             // Find all candidates for funcletization. Some sub-expressions are reduced to constants,
 | |
|             // others are reduced to variables. The rules vary based on the _mode.
 | |
|             Func<Expression, bool> isClientConstant;
 | |
|             Func<Expression, bool> isClientVariable;
 | |
| 
 | |
|             expression = ReplaceRootContextParameter(expression);
 | |
| 
 | |
|             if (_mode == Mode.CompiledQueryEvaluation)
 | |
|             {
 | |
|                 // We lock down closure expressions for compiled queries, so everything is either
 | |
|                 // a constant or a query parameter produced from the explicit parameters to the
 | |
|                 // compiled query delegate.
 | |
|                 isClientConstant = Nominate(expression, this.IsClosureExpression);
 | |
|                 isClientVariable = Nominate(expression, this.IsCompiledQueryParameterVariable);
 | |
|             }
 | |
|             else if (_mode == Mode.CompiledQueryLockdown)
 | |
|             {
 | |
|                 // When locking down a compiled query, we can evaluate all closure expressions.
 | |
|                 isClientConstant = Nominate(expression, this.IsClosureExpression);
 | |
|                 isClientVariable = (exp) => false;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 Debug.Assert(_mode == Mode.ConventionalQuery, "No other options...");
 | |
| 
 | |
|                 // There are no variable parameters outside of compiled queries, so everything is
 | |
|                 // either a constant or a closure expression.
 | |
|                 isClientConstant = Nominate(expression, this.IsImmutable);
 | |
|                 isClientVariable = Nominate(expression, this.IsClosureExpression);
 | |
|             }
 | |
| 
 | |
|             // Now rewrite given nomination functions
 | |
|             FuncletizingVisitor visitor = new FuncletizingVisitor(this, isClientConstant, isClientVariable);
 | |
|             Expression result = visitor.Visit(expression);
 | |
|             recompileRequired = visitor.GetRecompileRequiredFunction();
 | |
|             
 | |
|             return result;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Replaces context parameter (e.g. 'ctx' in CompiledQuery.Compile(ctx => ctx.Products)) with constant
 | |
|         /// containing the object context.
 | |
|         /// </summary>
 | |
|         private Expression ReplaceRootContextParameter(Expression expression)
 | |
|         {
 | |
|             if (null != _rootContextExpression)
 | |
|             {
 | |
|                 return EntityExpressionVisitor.Visit(
 | |
|                     expression, (exp, baseVisit) =>
 | |
|                     exp == _rootContextParameter ? _rootContextExpression : baseVisit(exp));
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 return expression;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns a function indicating whether the given expression and all of its children satisfy the 
 | |
|         /// 'localCriterion'.
 | |
|         /// </summary>
 | |
|         private static Func<Expression, bool> Nominate(Expression expression, Func<Expression, bool> localCriterion)
 | |
|         {
 | |
|             EntityUtil.CheckArgumentNull(localCriterion, "localCriterion");
 | |
|             HashSet<Expression> candidates = new HashSet<Expression>();
 | |
|             bool cannotBeNominated = false;
 | |
|             Func<Expression, Func<Expression, Expression>, Expression> visit = (exp, baseVisit) =>
 | |
|                 {
 | |
|                     if (exp != null)
 | |
|                     {
 | |
|                         bool saveCannotBeNominated = cannotBeNominated;
 | |
|                         cannotBeNominated = false;
 | |
|                         baseVisit(exp);
 | |
|                         if (!cannotBeNominated)
 | |
|                         {
 | |
|                             // everyone below me can be nominated, so
 | |
|                             // see if this one can be also
 | |
|                             if (localCriterion(exp))
 | |
|                             {
 | |
|                                 candidates.Add(exp);
 | |
|                             }
 | |
|                             else
 | |
|                             {
 | |
|                                 cannotBeNominated = true;
 | |
|                             }
 | |
|                         }
 | |
|                         cannotBeNominated |= saveCannotBeNominated;
 | |
|                     }
 | |
|                     return exp;
 | |
|                 };
 | |
|             EntityExpressionVisitor.Visit(expression, visit);
 | |
|             return candidates.Contains;
 | |
|         }
 | |
| 
 | |
|         private enum Mode
 | |
|         {
 | |
|             CompiledQueryLockdown,
 | |
|             CompiledQueryEvaluation,
 | |
|             ConventionalQuery,
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Determines whether the node may be evaluated locally and whether 
 | |
|         /// it is a constant. Assumes that all children are also client expressions.
 | |
|         /// </summary>
 | |
|         private bool IsImmutable(Expression expression)
 | |
|         {
 | |
|             if (null == expression) { return false; }
 | |
|             switch (expression.NodeType)
 | |
|             {
 | |
|                 case ExpressionType.New:
 | |
|                     {
 | |
|                         // support construction of primitive types
 | |
|                         PrimitiveType primitiveType;
 | |
|                         if (!ClrProviderManifest.Instance.TryGetPrimitiveType(TypeSystem.GetNonNullableType(expression.Type),
 | |
|                             out primitiveType))
 | |
|                         {
 | |
|                             return false;
 | |
|                         }
 | |
|                         return true;
 | |
|                     }
 | |
|                 case ExpressionType.Constant:
 | |
|                     return true;
 | |
|                 case ExpressionType.NewArrayInit:
 | |
|                     // allow initialization of byte[] 'literals'
 | |
|                     return (typeof(byte[]) == expression.Type);
 | |
|                 case ExpressionType.Convert:
 | |
|                     return true;
 | |
|                 default:
 | |
|                     return false;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Determines whether the node may be evaluated locally and whether 
 | |
|         /// it is a variable. Assumes that all children are also variable client expressions.
 | |
|         /// </summary>
 | |
|         private bool IsClosureExpression(Expression expression)
 | |
|         {
 | |
|             if (null == expression) { return false; }
 | |
|             if (IsImmutable(expression)) { return true; }
 | |
|             if (ExpressionType.MemberAccess == expression.NodeType)
 | |
|             {
 | |
|                 MemberExpression member = (MemberExpression)expression;
 | |
|                 if (member.Member.MemberType == MemberTypes.Property)
 | |
|                 {
 | |
|                     return ExpressionConverter.CanFuncletizePropertyInfo((PropertyInfo)member.Member);
 | |
|                 }
 | |
|                 return true;
 | |
|             }
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Determines whether the node may be evaluated as a compiled query parameter.
 | |
|         /// Assumes that all children are also eligible compiled query parameters.
 | |
|         /// </summary>
 | |
|         private bool IsCompiledQueryParameterVariable(Expression expression)
 | |
|         {
 | |
|             if (null == expression) { return false; }
 | |
|             if (IsClosureExpression(expression)) { return true; }
 | |
|             if (ExpressionType.Parameter == expression.NodeType)
 | |
|             {
 | |
|                 ParameterExpression parameter = (ParameterExpression)expression;
 | |
|                 return _compiledQueryParameters.Contains(parameter);
 | |
|             }
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Determine whether the given CLR type is legal for an ObjectParameter or constant
 | |
|         /// DbExpression.
 | |
|         /// </summary>
 | |
|         private bool TryGetTypeUsageForTerminal(Type type, out TypeUsage typeUsage)
 | |
|         {
 | |
|             EntityUtil.CheckArgumentNull(type, "type");
 | |
| 
 | |
|             if (_rootContext.Perspective.TryGetTypeByName(TypeSystem.GetNonNullableType(type).FullName,
 | |
|                 false, // bIgnoreCase
 | |
|                 out typeUsage) &&
 | |
|                 (TypeSemantics.IsScalarType(typeUsage)))
 | |
|             {
 | |
|                 return true;
 | |
|             }
 | |
| 
 | |
|             typeUsage = null;
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates the next available parameter name.
 | |
|         /// </summary>
 | |
|         internal string GenerateParameterName()
 | |
|         {
 | |
|             // To avoid collisions with user parameters (the full set is not
 | |
|             // known at this time) we plug together an 'unlikely' prefix and 
 | |
|             // a number.
 | |
|             return String.Format(CultureInfo.InvariantCulture, "{0}{1}",
 | |
|                 s_parameterPrefix,
 | |
|                 _parameterNumber++);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Walks the expression tree and replaces client-evaluable expressions with constants
 | |
|         /// or QueryParameterExpressions.
 | |
|         /// </summary>
 | |
|         private sealed class FuncletizingVisitor : EntityExpressionVisitor
 | |
|         {
 | |
|             private readonly Funcletizer _funcletizer;
 | |
|             private readonly Func<Expression, bool> _isClientConstant;
 | |
|             private readonly Func<Expression, bool> _isClientVariable;
 | |
|             private readonly List<Func<bool>> _recompileRequiredDelegates = new List<Func<bool>>();
 | |
| 
 | |
|             internal FuncletizingVisitor(
 | |
|                 Funcletizer funcletizer,
 | |
|                 Func<Expression, bool> isClientConstant,
 | |
|                 Func<Expression, bool> isClientVariable)
 | |
|             {
 | |
|                 EntityUtil.CheckArgumentNull(funcletizer, "funcletizer");
 | |
|                 EntityUtil.CheckArgumentNull(isClientConstant, "isClientConstant");
 | |
|                 EntityUtil.CheckArgumentNull(isClientVariable, "isClientVariable");
 | |
|             
 | |
|                 _funcletizer = funcletizer;
 | |
|                 _isClientConstant = isClientConstant;
 | |
|                 _isClientVariable = isClientVariable;
 | |
|             }
 | |
| 
 | |
|             /// <summary>
 | |
|             /// Returns a delegate indicating (when called) whether a change has been identified
 | |
|             /// requiring a complete recompile of the query.
 | |
|             /// </summary>
 | |
|             internal Func<bool> GetRecompileRequiredFunction()
 | |
|             {
 | |
|                 // assign list to local variable to avoid including the entire Funcletizer
 | |
|                 // class in the closure environment
 | |
|                 ReadOnlyCollection<Func<bool>> recompileRequiredDelegates = _recompileRequiredDelegates.AsReadOnly();
 | |
|                 return () => recompileRequiredDelegates.Any(d => d());
 | |
|             }
 | |
| 
 | |
|             internal override Expression Visit(Expression exp)
 | |
|             {
 | |
|                 if (exp != null)
 | |
|                 {
 | |
|                     if (!_funcletizer._linqExpressionStack.Add(exp))
 | |
|                     {
 | |
|                         // This expression is already in the stack.
 | |
|                         throw EntityUtil.InvalidOperation(Strings.ELinq_CycleDetected);
 | |
|                     }
 | |
| 
 | |
|                     try
 | |
|                     {
 | |
|                         if (_isClientConstant(exp))
 | |
|                         {
 | |
|                             return InlineValue(exp, false);
 | |
|                         }
 | |
|                         else if (_isClientVariable(exp))
 | |
|                         {
 | |
|                             TypeUsage queryParameterType;
 | |
|                             if (_funcletizer.TryGetTypeUsageForTerminal(exp.Type, out queryParameterType))
 | |
|                             {
 | |
|                                 DbParameterReferenceExpression parameterReference = queryParameterType.Parameter(_funcletizer.GenerateParameterName());
 | |
|                                 return new QueryParameterExpression(parameterReference, exp, _funcletizer._compiledQueryParameters);
 | |
|                             }
 | |
|                             else if (_funcletizer.IsCompiledQuery)
 | |
|                             {
 | |
|                                 throw InvalidCompiledQueryParameterException(exp);
 | |
|                             }
 | |
|                             else
 | |
|                             {
 | |
|                                 return InlineValue(exp, true);
 | |
|                             }
 | |
|                         }
 | |
|                         return base.Visit(exp);
 | |
|                     }
 | |
|                     finally
 | |
|                     {
 | |
|                         _funcletizer._linqExpressionStack.Remove(exp);
 | |
|                     }
 | |
|                 }
 | |
|                 return base.Visit(exp);
 | |
|             }
 | |
| 
 | |
|             private static NotSupportedException InvalidCompiledQueryParameterException(Expression expression)
 | |
|             {
 | |
|                 ParameterExpression parameterExp;
 | |
|                 if (expression.NodeType == ExpressionType.Parameter)
 | |
|                 {
 | |
|                     parameterExp = (ParameterExpression)expression;
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     // If this is a simple query parameter (involving a single delegate parameter) report the
 | |
|                     // type of that parameter. Otherwise, report the type of the part of the parameter.
 | |
|                     HashSet<ParameterExpression> parameters = new HashSet<ParameterExpression>();
 | |
|                     EntityExpressionVisitor.Visit(expression, (exp, baseVisit) =>
 | |
|                     {
 | |
|                         if (null != exp && exp.NodeType == ExpressionType.Parameter)
 | |
|                         {
 | |
|                             parameters.Add((ParameterExpression)exp);
 | |
|                         }
 | |
|                         return baseVisit(exp);
 | |
|                     });
 | |
| 
 | |
|                     if (parameters.Count != 1)
 | |
|                     {
 | |
|                         return EntityUtil.NotSupported(Strings.CompiledELinq_UnsupportedParameterTypes(expression.Type.FullName));
 | |
|                     }
 | |
|                     
 | |
|                     parameterExp = parameters.Single();
 | |
|                 }
 | |
| 
 | |
|                 if (parameterExp.Type.Equals(expression.Type))
 | |
|                 {
 | |
|                     // If the expression type is the same as the parameter type, indicate that the parameter type is not valid.
 | |
|                     return EntityUtil.NotSupported(Strings.CompiledELinq_UnsupportedNamedParameterType(parameterExp.Name, parameterExp.Type.FullName));                    
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     // Otherwise, indicate that using the specified parameter to produce a value of the expression's type is not supported in compiled query
 | |
|                     return EntityUtil.NotSupported(Strings.CompiledELinq_UnsupportedNamedParameterUseAsType(parameterExp.Name, expression.Type.FullName));
 | |
|                 }
 | |
|             }
 | |
|         
 | |
|             /// <summary>
 | |
|             /// Compiles a delegate returning the value of the given expression.
 | |
|             /// </summary>
 | |
|             private Func<object> CompileExpression(Expression expression)
 | |
|             {
 | |
|                 Func<object> func = Expression
 | |
|                     .Lambda<Func<object>>(TypeSystem.EnsureType(expression, typeof(object)))
 | |
|                     .Compile();
 | |
|                 return func;
 | |
|             }
 | |
| 
 | |
|             /// <summary>
 | |
|             /// Inlines a funcletizable expression. Queries and lambda expressions are expanded
 | |
|             /// inline. All other values become simple constants.
 | |
|             /// </summary>
 | |
|             private Expression InlineValue(Expression expression, bool recompileOnChange)
 | |
|             {
 | |
|                 Func<object> getValue = null;
 | |
|                 object value = null;
 | |
|                 if (expression.NodeType == ExpressionType.Constant)
 | |
|                 {
 | |
|                     value = ((ConstantExpression)expression).Value;
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     bool fastPath = false;
 | |
|                     //fastpath to process object query
 | |
|                     if (expression.NodeType == ExpressionType.Convert)
 | |
|                     {
 | |
|                         var ue = (UnaryExpression)expression;
 | |
|                         // The ObjectSet instance is wrapped inside Convert UnaryExpression in 
 | |
|                         // ElinqQueryState.GetExpression(). The block below identifies such an 
 | |
|                         // expression, makes sure the object query it contains is immutable and 
 | |
|                         // extracts the reference to the object query.
 | |
|                         if (!recompileOnChange
 | |
|                             && ue.Operand.NodeType == ExpressionType.Constant
 | |
|                             && typeof(IQueryable).IsAssignableFrom(ue.Operand.Type))
 | |
|                         {
 | |
|                             value = ((ConstantExpression)ue.Operand).Value;
 | |
|                             fastPath = true;
 | |
|                         }
 | |
|                     }
 | |
|                     if (!fastPath)
 | |
|                     {
 | |
|                         getValue = CompileExpression(expression);
 | |
|                         value = getValue();
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 Expression result = null;
 | |
|                 ObjectQuery inlineQuery = value as ObjectQuery;
 | |
|                 if (inlineQuery != null)
 | |
|                 {
 | |
|                     result = InlineObjectQuery(inlineQuery, expression.Type);
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     LambdaExpression lambda = value as LambdaExpression;
 | |
|                     if (null != lambda)
 | |
|                     {
 | |
|                         result = InlineExpression(Expression.Quote(lambda));
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         // everything else is just a constant...
 | |
|                         result = expression.NodeType == ExpressionType.Constant
 | |
|                             ? expression
 | |
|                             : Expression.Constant(value, expression.Type);
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 if (recompileOnChange)
 | |
|                 {
 | |
|                     AddRecompileRequiredDelegates(getValue, value);
 | |
|                 }
 | |
| 
 | |
|                 return result;
 | |
|             }
 | |
| 
 | |
|             private void AddRecompileRequiredDelegates(Func<object> getValue, object value)
 | |
|             {
 | |
|                 // Build a delegate that returns true when the inline value has changed.
 | |
|                 // Outside of ObjectQuery, this amounts to a reference comparison.
 | |
|                 ObjectQuery originalQuery = value as ObjectQuery;
 | |
|                 if (null != originalQuery)
 | |
|                 {
 | |
|                     // For inline queries, we need to check merge options as well (it's mutable)
 | |
|                     MergeOption? originalMergeOption = originalQuery.QueryState.UserSpecifiedMergeOption;
 | |
|                     if (null == getValue)
 | |
|                     {
 | |
|                         _recompileRequiredDelegates.Add(() => originalQuery.QueryState.UserSpecifiedMergeOption != originalMergeOption);
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         _recompileRequiredDelegates.Add(() =>
 | |
|                         {
 | |
|                             ObjectQuery currentQuery = getValue() as ObjectQuery;
 | |
|                             return !object.ReferenceEquals(originalQuery, currentQuery) ||
 | |
|                                 currentQuery.QueryState.UserSpecifiedMergeOption != originalMergeOption;
 | |
|                         });
 | |
| 
 | |
|                     }
 | |
|                 }
 | |
|                 else if (null != getValue)
 | |
|                 {
 | |
|                     _recompileRequiredDelegates.Add(() => !object.ReferenceEquals(value, getValue()));
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             /// <summary>
 | |
|             /// Gets the appropriate LINQ expression for an inline ObjectQuery instance.
 | |
|             /// </summary>
 | |
|             private Expression InlineObjectQuery(ObjectQuery inlineQuery, Type expressionType)
 | |
|             {
 | |
|                 EntityUtil.CheckArgumentNull(inlineQuery, "inlineQuery");
 | |
| 
 | |
|                 Expression queryExpression;
 | |
|                 if (_funcletizer._mode == Mode.CompiledQueryLockdown)
 | |
|                 {
 | |
|                     // In the lockdown phase, we don't chase down inline object queries because
 | |
|                     // we don't yet know what the object context is supposed to be.
 | |
|                     queryExpression = Expression.Constant(inlineQuery, expressionType);
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     if (!object.ReferenceEquals(_funcletizer._rootContext, inlineQuery.QueryState.ObjectContext))
 | |
|                     {
 | |
|                         throw EntityUtil.NotSupported(System.Data.Entity.Strings.ELinq_UnsupportedDifferentContexts);
 | |
|                     }
 | |
| 
 | |
|                     queryExpression = inlineQuery.GetExpression();
 | |
| 
 | |
|                     // If it's not an entity-sql (terminal) query, recursively process
 | |
|                     if (!(inlineQuery.QueryState is EntitySqlQueryState))
 | |
|                     {
 | |
|                         queryExpression = InlineExpression(queryExpression);
 | |
|                     }
 | |
| 
 | |
|                     queryExpression = TypeSystem.EnsureType(queryExpression, expressionType);
 | |
|                 }
 | |
| 
 | |
|                 return queryExpression;
 | |
|             }
 | |
| 
 | |
|             private Expression InlineExpression(Expression exp)
 | |
|             {
 | |
|                 Func<bool> inlineExpressionRequiresRecompile;
 | |
|                 exp = _funcletizer.Funcletize(exp, out inlineExpressionRequiresRecompile);
 | |
|                 if (!_funcletizer.IsCompiledQuery)
 | |
|                 {
 | |
|                     _recompileRequiredDelegates.Add(inlineExpressionRequiresRecompile);
 | |
|                 }
 | |
|                 return exp;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// A LINQ expression corresponding to a query parameter.
 | |
|     /// </summary>
 | |
|     internal sealed class QueryParameterExpression : Expression
 | |
|     {
 | |
|         private readonly DbParameterReferenceExpression _parameterReference;
 | |
|         private readonly Type _type;
 | |
|         private readonly Expression _funcletizedExpression;
 | |
|         private readonly IEnumerable<ParameterExpression> _compiledQueryParameters;
 | |
|         private Delegate _cachedDelegate;
 | |
| 
 | |
|         internal QueryParameterExpression(
 | |
|             DbParameterReferenceExpression parameterReference,
 | |
|             Expression funcletizedExpression,
 | |
|             IEnumerable<ParameterExpression> compiledQueryParameters)
 | |
|         {
 | |
|             EntityUtil.CheckArgumentNull(parameterReference, "parameterReference");
 | |
|             EntityUtil.CheckArgumentNull(funcletizedExpression, "funcletizedExpression");
 | |
|             _compiledQueryParameters = compiledQueryParameters ?? Enumerable.Empty<ParameterExpression>();
 | |
|             _parameterReference = parameterReference;
 | |
|             _type = funcletizedExpression.Type;
 | |
|             _funcletizedExpression = funcletizedExpression;
 | |
|             _cachedDelegate = null;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Gets the current value of the parameter given (optional) compiled query arguments.
 | |
|         /// </summary>
 | |
|         internal object EvaluateParameter(object[] arguments)
 | |
|         {
 | |
|             if (_cachedDelegate == null)
 | |
|             {
 | |
|                 if (_funcletizedExpression.NodeType == ExpressionType.Constant)
 | |
|                 {
 | |
|                     return ((ConstantExpression)_funcletizedExpression).Value;
 | |
|                 }
 | |
|                 ConstantExpression ce;
 | |
|                 if (TryEvaluatePath(_funcletizedExpression, out ce))
 | |
|                 {
 | |
|                     return ce.Value;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             try
 | |
|             {
 | |
|                 if (_cachedDelegate == null)
 | |
|                 {
 | |
|                     // Get the Func<> type for the property evaluator
 | |
|                     Type delegateType = TypeSystem.GetDelegateType(_compiledQueryParameters.Select(p => p.Type), _type);
 | |
| 
 | |
|                     // Now compile delegate for the funcletized expression
 | |
|                     _cachedDelegate = Expression.Lambda(delegateType, _funcletizedExpression, _compiledQueryParameters).Compile();
 | |
|                 }
 | |
|                 return _cachedDelegate.DynamicInvoke(arguments);
 | |
|             }
 | |
|             catch (TargetInvocationException e)
 | |
|             {
 | |
|                 throw e.InnerException;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Create QueryParameterExpression based on this one, but with the funcletized expression
 | |
|         /// wrapped by the given method
 | |
|         /// </summary>
 | |
|         /// <param name="method"></param>
 | |
|         /// <returns></returns>
 | |
|         internal QueryParameterExpression EscapeParameterForLike(Func<string, string> method)
 | |
|         {
 | |
|             Expression wrappedExpression = Expression.Invoke(Expression.Constant(method), this._funcletizedExpression);
 | |
|             return new QueryParameterExpression(this._parameterReference, wrappedExpression, this._compiledQueryParameters);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Gets the parameter reference for the parameter.
 | |
|         /// </summary>
 | |
|         internal DbParameterReferenceExpression ParameterReference
 | |
|         {
 | |
|             get { return _parameterReference; }
 | |
|         }
 | |
| 
 | |
|         public override Type Type
 | |
|         {
 | |
|             get { return _type; }
 | |
|         }
 | |
| 
 | |
|         public override ExpressionType NodeType
 | |
|         {
 | |
|             get { return EntityExpressionVisitor.CustomExpression; }
 | |
|         }
 | |
| 
 | |
|         private bool TryEvaluatePath(Expression expression, out ConstantExpression constantExpression)
 | |
|         {
 | |
|             MemberExpression me = expression as MemberExpression;
 | |
|             constantExpression = null;
 | |
|             if (me != null)
 | |
|             {
 | |
|                 Stack<MemberExpression> stack = new Stack<MemberExpression>();
 | |
|                 stack.Push(me);
 | |
|                 while ((me = me.Expression as MemberExpression) != null)
 | |
|                 {
 | |
|                     stack.Push(me);
 | |
|                 }
 | |
|                 me = stack.Pop();
 | |
|                 ConstantExpression ce = me.Expression as ConstantExpression;
 | |
|                 if (ce != null)
 | |
|                 {
 | |
|                     object memberVal;
 | |
|                     if (!TryGetFieldOrPropertyValue(me, ((ConstantExpression)me.Expression).Value, out memberVal))
 | |
|                     {
 | |
|                         return false;
 | |
|                     }
 | |
|                     if (stack.Count > 0)
 | |
|                     {
 | |
|                         foreach (var rec in stack)
 | |
|                         {
 | |
|                             if (!TryGetFieldOrPropertyValue(rec, memberVal, out memberVal))
 | |
|                             {
 | |
|                                 return false;
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                     constantExpression = Expression.Constant(memberVal, expression.Type);
 | |
|                     return true;
 | |
|                 }
 | |
|             }
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         private bool TryGetFieldOrPropertyValue(MemberExpression me, object instance, out object memberValue)
 | |
|         {
 | |
|             bool result = false;
 | |
|             memberValue = null;
 | |
| 
 | |
|             try
 | |
|             {
 | |
|                 if (me.Member.MemberType == MemberTypes.Field)
 | |
|                 {
 | |
|                     memberValue = ((FieldInfo)me.Member).GetValue(instance);
 | |
|                     result = true;
 | |
|                 }
 | |
|                 else if (me.Member.MemberType == MemberTypes.Property)
 | |
|                 {
 | |
|                     memberValue = ((PropertyInfo)me.Member).GetValue(instance, null);
 | |
|                     result = true;
 | |
|                 }
 | |
|                 return result;
 | |
|             }
 | |
|             catch (TargetInvocationException ex)
 | |
|             {
 | |
|                 throw ex.InnerException;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 |