CompiledELinqQueryState.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / ndp / fx / src / DataEntity / System / Data / Objects / ELinq / CompiledELinqQueryState.cs / 1 / CompiledELinqQueryState.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner       [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 

namespace System.Data.Objects.ELinq 
{
    using System;
    using System.Collections.Generic;
    using System.Data.Common.CommandTrees; 
    using System.Data.Objects;
    using System.Data.Objects.ELinq; 
    using System.Data.Objects.Internal; 
    using System.Diagnostics;
    using System.Linq; 

    using CqtExpression = System.Data.Common.CommandTrees.DbExpression;
    using LinqExpression = System.Linq.Expressions.Expression;
    using System.Data.Metadata.Edm; 
    using System.Linq.Expressions;
    using System.Data.Entity; 
    using System.Reflection; 
    using System.Data.Common.QueryCache;
 
    /// 
    /// Models a compiled Linq to Entities ObjectQuery
    /// 
    internal sealed class CompiledELinqQueryState : ELinqQueryState 
    {
        private readonly ParameterExpression _rootContextParameter; 
        private readonly CompiledQueryParameter[] _parameters; 
        private readonly Guid _cacheToken;
        private CompiledQueryCacheEntry _cacheEntry; 

        /// 
        /// Factory method to create a new compiled query state instance
        ///  
        /// The element type of the new instance (the 'T' of the ObjectQuery<T> that the new state instance will back)"
        /// The object context with which the new instance should be associated 
        /// The compiled query definition, as a  
        /// The parameters that are available to the new compiled query state instance; correspond with the lambda parameters
        /// The cache token to use when retrieving or storing the new instance's execution plan in the query cache 
        /// The new compiled query state instance
        internal static CompiledELinqQueryState Create(Type elementType, ObjectContext context, LambdaExpression lambda, Dictionary objectParameters, Guid cacheToken)
        {
            EntityUtil.CheckArgumentNull(context, "context"); 

            ObjectParameterCollection objectQueryParameters = new ObjectParameterCollection(context.Perspective); 
            CompiledQueryParameter[] compiledParams = new CompiledQueryParameter[objectParameters.Count]; 
            int i = 0;
            foreach (KeyValuePair objectParameter in objectParameters) 
            {
                compiledParams[i] = new CompiledQueryParameter(objectParameter.Key, objectParameter.Value);
                try
                { 
                    objectQueryParameters.Add(compiledParams[i].ObjectParameter);
                } 
                catch (ArgumentOutOfRangeException) 
                {
                    // 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 parameters = ParameterExpressionVisitor.FindAllParametersInExpression(objectParameter.Key);
                    Type parameterType = parameters.Count == 1 ? parameters.Single().Type : objectParameter.Value.ParameterType;
                    throw EntityUtil.NotSupported(Strings.CompiledELinq_UnsupportedParameterType(parameterType.FullName)); 
                }
                i++; 
            } 

            return new CompiledELinqQueryState(elementType, context, objectQueryParameters, compiledParams, lambda, cacheToken); 
        }

        private CompiledELinqQueryState(Type elementType, ObjectContext context, ObjectParameterCollection objectParameters, CompiledQueryParameter[] compiledParams, LambdaExpression lambda, Guid cacheToken)
            : base(elementType, context, objectParameters, lambda.Body) 
        {
            Debug.Assert(objectParameters != null, "A parameter collection is required"); 
 
            _rootContextParameter = lambda.Parameters[0];
            _parameters = compiledParams; 
            _cacheToken = cacheToken;

            // The parameters available to a compiled query are fixed.
            this.EnsureParameters().SetReadOnly(true); 
        }
 
        internal override ObjectQueryExecutionPlan GetExecutionPlan(MergeOption? forMergeOption) 
        {
            Debug.Assert(this.Span == null, "Include span specified on compiled LINQ-based ObjectQuery instead of within the expression tree?"); 
            Debug.Assert(this._cachedPlan == null, "Cached plan should not be set on compiled LINQ queries");

            // Metadata is required to generate the execution plan or to retrieve it from the cache.
            this.ObjectContext.EnsureMetadata(); 

            ObjectQueryExecutionPlan plan = null; 
            CompiledQueryCacheEntry cacheEntry = this._cacheEntry; 
            if (cacheEntry != null)
            { 
                // The cache entry has already been retrieved, so compute the effective merge option with the following precedence:
                // 1. The merge option specified as the argument to Execute(MergeOption), and so to this method
                // 2. The merge option set using ObjectQuery.MergeOption
                // 3. The propagated merge option as recorded in the cache entry 
                // 4. The global default merge option.
                MergeOption mergeOption = EnsureMergeOption(forMergeOption, this.UserSpecifiedMergeOption, cacheEntry.PropagatedMergeOption); 
 
                // Ask for the corresponding execution plan
                plan = cacheEntry.GetExecutionPlan(mergeOption); 
                if (plan == null)
                {
                    // Convert the LINQ expression to produce a command tree
                    ExpressionConverter converter = this.CreateExpressionConverter(); 
                    DbExpression queryExpression = converter.Convert();
                    DbQueryCommandTree tree = (DbQueryCommandTree)queryExpression.CommandTree; 
                    tree.Query = queryExpression; 

                    // Prepare the execution plan using the command tree and the computed effective merge option 
                    plan = ObjectQueryExecutionPlan.Prepare(this.ObjectContext, tree, this.ElementType, mergeOption, converter.PropagatedSpan);

                    // Update and retrieve the execution plan
                    plan = cacheEntry.SetExecutionPlan(plan); 
                }
            } 
            else 
            {
                // This instance does not yet have a reference to a cache entry. 
                // First, attempt to retrieve an existing cache entry.
                QueryCacheManager cacheManager = this.ObjectContext.MetadataWorkspace.GetQueryCacheManager();
                CompiledQueryCacheKey cacheKey = new CompiledQueryCacheKey(this._cacheToken);
 
                if (cacheManager.TryCacheLookup(cacheKey, out cacheEntry))
                { 
                    // An entry was found in the cache, so compute the effective merge option based on its propagated merge option, 
                    // and attempt to retrieve the corresponding execution plan.
                    this._cacheEntry = cacheEntry; 
                    MergeOption mergeOption = EnsureMergeOption(forMergeOption, this.UserSpecifiedMergeOption, cacheEntry.PropagatedMergeOption);
                    plan = cacheEntry.GetExecutionPlan(mergeOption);
                }
 
                // If no cache entry was found or if the cache entry did not contain the required execution plan, the plan is still null at this point.
                if (plan == null) 
                { 
                    // The execution plan needs to be produced, so create an appropriate expression converter and generate the query command tree.
                    ExpressionConverter converter = this.CreateExpressionConverter(); 
                    DbExpression queryExpression = converter.Convert();
                    DbQueryCommandTree tree = (DbQueryCommandTree)queryExpression.CommandTree;
                    tree.Query = queryExpression;
 
                    // If a cache entry for this compiled query's cache key was not successfully retrieved, then it must be created now.
                    // Note that this is only possible after converting the LINQ expression and discovering the propagated merge option, 
                    // which is required in order to create the cache entry. 
                    if (cacheEntry == null)
                    { 
                        // Create the cache entry using this instance's cache token and the propagated merge option (which may be null)
                        cacheEntry = new CompiledQueryCacheEntry(cacheKey, converter.PropagatedMergeOption);

                        // Attempt to add the entry to the cache. If an entry was added in the meantime, use that entry instead. 
                        QueryCacheEntry foundEntry;
                        if (cacheManager.TryLookupAndAdd(cacheEntry, out foundEntry)) 
                        { 
                            cacheEntry = (CompiledQueryCacheEntry)foundEntry;
                        } 

                        // We now have a cache entry, so hold onto it for future use.
                        this._cacheEntry = cacheEntry;
                    } 

                    // Recompute the effective merge option in case a cache entry was just constructed above 
                    MergeOption mergeOption = EnsureMergeOption(forMergeOption, this.UserSpecifiedMergeOption, cacheEntry.PropagatedMergeOption); 

                    // Ask the (retrieved or constructed) cache entry for the corresponding execution plan. 
                    plan = cacheEntry.GetExecutionPlan(mergeOption);
                    if (plan == null)
                    {
                        // The plan is not present, so prepare it now using the computed effective merge option 
                        plan = ObjectQueryExecutionPlan.Prepare(this.ObjectContext, tree, this.ElementType, mergeOption, converter.PropagatedSpan);
 
                        // Update the execution plan for the merge option on the cache entry. 
                        // If the execution plan was set in the meantime, SetExecutionPlan will return that value, otherwise it will return 'plan'.
                        plan = cacheEntry.SetExecutionPlan(plan); 
                    }
                }
            }
 
            Debug.Assert(plan != null, "Failed to produce an execution plan?");
            return plan; 
        } 

        ///  
        /// Overrides GetResultType and attempts to first retrieve the result type from the cache entry.
        /// 
        /// The query result type from this compiled query's cache entry, if possible; otherwise defers to 
        protected override TypeUsage GetResultType() 
        {
            CompiledQueryCacheEntry cacheEntry = this._cacheEntry; 
            TypeUsage resultType; 
            if (cacheEntry != null &&
                cacheEntry.TryGetResultType(out resultType)) 
            {
                return resultType;
            }
 
            return base.GetResultType();
        } 
 
        /// 
        /// Gets a LINQ expression that defines this query. 
        /// This is overridden to remove parameter references from the underlying expression,
        /// producing an expression that contains the values of those parameters as s.
        /// 
        internal override Expression Expression 
        {
            get 
            { 
                return CreateDonateableExpressionVisitor.Replace(base.Expression, ObjectContext, _rootContextParameter, _parameters);
            } 
        }

        /// 
        /// Overrides CreateExpressionConverter to return a converter that uses a binding context based on the compiled query parameters, 
        /// rather than a default binding context.
        ///  
        /// An expression converter appropriate for converting this compiled query state instance 
        protected override ExpressionConverter CreateExpressionConverter()
        { 
            // Create the command tree used for conversion and add the fixed set of parameters to it
            DbQueryCommandTree tree = new DbQueryCommandTree(this.ObjectContext.MetadataWorkspace, DataSpace.CSpace);
            foreach (var parameter in _parameters)
            { 
                parameter.CreateParameterReferenceAndAddParameterToCommandTree(tree, this.ObjectContext.Perspective);
            } 
 
            // Initialize a binding context based on the compiled query parameters.
            BindingContext context = new BindingContext(_rootContextParameter, this.ObjectContext, _parameters); 

            // Return a new expression converter that uses the initialized command tree and binding context.
            return new ExpressionConverter(this.ObjectContext, context, tree, base.Expression, this.Parameters);
        } 

        ///  
        /// Replaces ParameterExpresion with ConstantExpression 
        /// to make the expression usable as a donor expression
        ///  
        private sealed class CreateDonateableExpressionVisitor : ExpressionVisitor
        {
            private readonly Dictionary _parameterToValueLookup;
            private readonly BindingContext _bindingContext; 

            private CreateDonateableExpressionVisitor(BindingContext bindingContext, Dictionary parameterToValueLookup) 
            { 
                Debug.Assert(bindingContext != null, "bindingContext must have a value");
                Debug.Assert(parameterToValueLookup != null, "parameterToValueLookup must have a value"); 
                _bindingContext = bindingContext;
                _parameterToValueLookup = parameterToValueLookup;
            }
 
            internal static Expression Replace(Expression input, ObjectContext objectContext, ParameterExpression rootContextParameter, CompiledQueryParameter [] parameters)
            { 
                BindingContext bindingContext = new BindingContext(rootContextParameter, objectContext, parameters); 
                Dictionary parameterLookup = parameters.ToDictionary(p => p.Expression, p => p.ObjectParameter.Value);
                var replacer = new CreateDonateableExpressionVisitor(bindingContext, parameterLookup); 
                return replacer.Visit(input);
            }

            ///  
            /// Replace all the known (outer scope) Expressions with ConstantExpressions
            ///  
            /// The Expression to replace 
            /// The new constant expression
            internal override Expression Visit(Expression expression) 
            {
                object value;
                if (null != expression && _parameterToValueLookup.TryGetValue(expression, out value))
                { 
                    return Expression.Constant(value, expression.Type);
                } 
 
                return base.Visit(expression);
            } 

            internal override Expression VisitMethodCall(MethodCallExpression m)
            {
                Expression newExpression; 
                if (TryReplaceRootQuery(m, out newExpression))
                { 
                    return newExpression; 
                }
 
                return base.VisitMethodCall(m);
            }

            internal override Expression VisitMemberAccess(MemberExpression m) 
            {
                Expression newExpression; 
                if (TryReplaceRootQuery(m, out newExpression)) 
                {
                    return newExpression; 
                }
                return base.VisitMemberAccess(m);
            }
 
            bool TryReplaceRootQuery(Expression expression, out Expression newExpression)
            { 
                ObjectQuery rootQuery; 
                if (ExpressionEvaluator.TryEvaluateRootQuery(_bindingContext, expression, out rootQuery))
                { 
                    Debug.Assert(typeof(ObjectQuery).IsAssignableFrom(expression.Type), "how did we get a different type");
                    newExpression = Expression.Constant(rootQuery, expression.Type);
                    return true;
                } 

                newExpression = null; 
                return false; 
            }
        } 

        /// 
        /// Collects parameters expressions from a LINQ expression tree.
        ///  
        private sealed class ParameterExpressionVisitor : ExpressionVisitor
        { 
            internal readonly HashSet _parameters = new HashSet(); 

            private ParameterExpressionVisitor() 
            {
            }

            internal static HashSet FindAllParametersInExpression(Expression expression) 
            {
                ParameterExpressionVisitor visitor = new ParameterExpressionVisitor(); 
                visitor.Visit(expression); 
                return visitor._parameters;
            } 

            internal override Expression VisitParameter(ParameterExpression p)
            {
                _parameters.Add(p); 
                return base.VisitParameter(p);
            } 
        } 
    }
 
    internal sealed class CompiledQueryParameter
    {
        private readonly Expression _expression;
        private readonly ObjectParameter _parameter; 

        private DbParameterReferenceExpression _parameterReference; 
 
        internal CompiledQueryParameter(Expression expression, ObjectParameter parameter)
        { 
            _expression = expression;
            _parameter = parameter;
        }
 
        /// 
        /// Adds the ObjectParameter to the DbCommandTree, and creates a ParameterReferenceExpression 
        /// to be placed in the proper places in the tree 
        /// 
        /// The command tree to create operate on. 
        /// The ClrPerspective to help get the correct TypeUsage.
        internal void CreateParameterReferenceAndAddParameterToCommandTree(DbCommandTree commandTree, ClrPerspective perspective)
        {
            Debug.Assert(_parameterReference == null, "don't call this once it is already setup"); 

            TypeUsage typeUsage; 
            if(ClosureBinding.TryGetTypeUsageForObjectParameter(_parameter, perspective, out typeUsage)) 
            {
                commandTree.AddParameter(_parameter.Name, typeUsage); 
                _parameterReference = commandTree.CreateParameterReferenceExpression(_parameter.Name);
            }

            Debug.Assert(_parameterReference != null, "Not sure why this would ever happen, and it isn't handled"); 
        }
 
        internal Expression Expression { get { return _expression; } } 
        internal ObjectParameter ObjectParameter { get { return _parameter; } }
        internal DbParameterReferenceExpression ParameterReference { get { return _parameterReference; } } 
    }

}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner       [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 

namespace System.Data.Objects.ELinq 
{
    using System;
    using System.Collections.Generic;
    using System.Data.Common.CommandTrees; 
    using System.Data.Objects;
    using System.Data.Objects.ELinq; 
    using System.Data.Objects.Internal; 
    using System.Diagnostics;
    using System.Linq; 

    using CqtExpression = System.Data.Common.CommandTrees.DbExpression;
    using LinqExpression = System.Linq.Expressions.Expression;
    using System.Data.Metadata.Edm; 
    using System.Linq.Expressions;
    using System.Data.Entity; 
    using System.Reflection; 
    using System.Data.Common.QueryCache;
 
    /// 
    /// Models a compiled Linq to Entities ObjectQuery
    /// 
    internal sealed class CompiledELinqQueryState : ELinqQueryState 
    {
        private readonly ParameterExpression _rootContextParameter; 
        private readonly CompiledQueryParameter[] _parameters; 
        private readonly Guid _cacheToken;
        private CompiledQueryCacheEntry _cacheEntry; 

        /// 
        /// Factory method to create a new compiled query state instance
        ///  
        /// The element type of the new instance (the 'T' of the ObjectQuery<T> that the new state instance will back)"
        /// The object context with which the new instance should be associated 
        /// The compiled query definition, as a  
        /// The parameters that are available to the new compiled query state instance; correspond with the lambda parameters
        /// The cache token to use when retrieving or storing the new instance's execution plan in the query cache 
        /// The new compiled query state instance
        internal static CompiledELinqQueryState Create(Type elementType, ObjectContext context, LambdaExpression lambda, Dictionary objectParameters, Guid cacheToken)
        {
            EntityUtil.CheckArgumentNull(context, "context"); 

            ObjectParameterCollection objectQueryParameters = new ObjectParameterCollection(context.Perspective); 
            CompiledQueryParameter[] compiledParams = new CompiledQueryParameter[objectParameters.Count]; 
            int i = 0;
            foreach (KeyValuePair objectParameter in objectParameters) 
            {
                compiledParams[i] = new CompiledQueryParameter(objectParameter.Key, objectParameter.Value);
                try
                { 
                    objectQueryParameters.Add(compiledParams[i].ObjectParameter);
                } 
                catch (ArgumentOutOfRangeException) 
                {
                    // 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 parameters = ParameterExpressionVisitor.FindAllParametersInExpression(objectParameter.Key);
                    Type parameterType = parameters.Count == 1 ? parameters.Single().Type : objectParameter.Value.ParameterType;
                    throw EntityUtil.NotSupported(Strings.CompiledELinq_UnsupportedParameterType(parameterType.FullName)); 
                }
                i++; 
            } 

            return new CompiledELinqQueryState(elementType, context, objectQueryParameters, compiledParams, lambda, cacheToken); 
        }

        private CompiledELinqQueryState(Type elementType, ObjectContext context, ObjectParameterCollection objectParameters, CompiledQueryParameter[] compiledParams, LambdaExpression lambda, Guid cacheToken)
            : base(elementType, context, objectParameters, lambda.Body) 
        {
            Debug.Assert(objectParameters != null, "A parameter collection is required"); 
 
            _rootContextParameter = lambda.Parameters[0];
            _parameters = compiledParams; 
            _cacheToken = cacheToken;

            // The parameters available to a compiled query are fixed.
            this.EnsureParameters().SetReadOnly(true); 
        }
 
        internal override ObjectQueryExecutionPlan GetExecutionPlan(MergeOption? forMergeOption) 
        {
            Debug.Assert(this.Span == null, "Include span specified on compiled LINQ-based ObjectQuery instead of within the expression tree?"); 
            Debug.Assert(this._cachedPlan == null, "Cached plan should not be set on compiled LINQ queries");

            // Metadata is required to generate the execution plan or to retrieve it from the cache.
            this.ObjectContext.EnsureMetadata(); 

            ObjectQueryExecutionPlan plan = null; 
            CompiledQueryCacheEntry cacheEntry = this._cacheEntry; 
            if (cacheEntry != null)
            { 
                // The cache entry has already been retrieved, so compute the effective merge option with the following precedence:
                // 1. The merge option specified as the argument to Execute(MergeOption), and so to this method
                // 2. The merge option set using ObjectQuery.MergeOption
                // 3. The propagated merge option as recorded in the cache entry 
                // 4. The global default merge option.
                MergeOption mergeOption = EnsureMergeOption(forMergeOption, this.UserSpecifiedMergeOption, cacheEntry.PropagatedMergeOption); 
 
                // Ask for the corresponding execution plan
                plan = cacheEntry.GetExecutionPlan(mergeOption); 
                if (plan == null)
                {
                    // Convert the LINQ expression to produce a command tree
                    ExpressionConverter converter = this.CreateExpressionConverter(); 
                    DbExpression queryExpression = converter.Convert();
                    DbQueryCommandTree tree = (DbQueryCommandTree)queryExpression.CommandTree; 
                    tree.Query = queryExpression; 

                    // Prepare the execution plan using the command tree and the computed effective merge option 
                    plan = ObjectQueryExecutionPlan.Prepare(this.ObjectContext, tree, this.ElementType, mergeOption, converter.PropagatedSpan);

                    // Update and retrieve the execution plan
                    plan = cacheEntry.SetExecutionPlan(plan); 
                }
            } 
            else 
            {
                // This instance does not yet have a reference to a cache entry. 
                // First, attempt to retrieve an existing cache entry.
                QueryCacheManager cacheManager = this.ObjectContext.MetadataWorkspace.GetQueryCacheManager();
                CompiledQueryCacheKey cacheKey = new CompiledQueryCacheKey(this._cacheToken);
 
                if (cacheManager.TryCacheLookup(cacheKey, out cacheEntry))
                { 
                    // An entry was found in the cache, so compute the effective merge option based on its propagated merge option, 
                    // and attempt to retrieve the corresponding execution plan.
                    this._cacheEntry = cacheEntry; 
                    MergeOption mergeOption = EnsureMergeOption(forMergeOption, this.UserSpecifiedMergeOption, cacheEntry.PropagatedMergeOption);
                    plan = cacheEntry.GetExecutionPlan(mergeOption);
                }
 
                // If no cache entry was found or if the cache entry did not contain the required execution plan, the plan is still null at this point.
                if (plan == null) 
                { 
                    // The execution plan needs to be produced, so create an appropriate expression converter and generate the query command tree.
                    ExpressionConverter converter = this.CreateExpressionConverter(); 
                    DbExpression queryExpression = converter.Convert();
                    DbQueryCommandTree tree = (DbQueryCommandTree)queryExpression.CommandTree;
                    tree.Query = queryExpression;
 
                    // If a cache entry for this compiled query's cache key was not successfully retrieved, then it must be created now.
                    // Note that this is only possible after converting the LINQ expression and discovering the propagated merge option, 
                    // which is required in order to create the cache entry. 
                    if (cacheEntry == null)
                    { 
                        // Create the cache entry using this instance's cache token and the propagated merge option (which may be null)
                        cacheEntry = new CompiledQueryCacheEntry(cacheKey, converter.PropagatedMergeOption);

                        // Attempt to add the entry to the cache. If an entry was added in the meantime, use that entry instead. 
                        QueryCacheEntry foundEntry;
                        if (cacheManager.TryLookupAndAdd(cacheEntry, out foundEntry)) 
                        { 
                            cacheEntry = (CompiledQueryCacheEntry)foundEntry;
                        } 

                        // We now have a cache entry, so hold onto it for future use.
                        this._cacheEntry = cacheEntry;
                    } 

                    // Recompute the effective merge option in case a cache entry was just constructed above 
                    MergeOption mergeOption = EnsureMergeOption(forMergeOption, this.UserSpecifiedMergeOption, cacheEntry.PropagatedMergeOption); 

                    // Ask the (retrieved or constructed) cache entry for the corresponding execution plan. 
                    plan = cacheEntry.GetExecutionPlan(mergeOption);
                    if (plan == null)
                    {
                        // The plan is not present, so prepare it now using the computed effective merge option 
                        plan = ObjectQueryExecutionPlan.Prepare(this.ObjectContext, tree, this.ElementType, mergeOption, converter.PropagatedSpan);
 
                        // Update the execution plan for the merge option on the cache entry. 
                        // If the execution plan was set in the meantime, SetExecutionPlan will return that value, otherwise it will return 'plan'.
                        plan = cacheEntry.SetExecutionPlan(plan); 
                    }
                }
            }
 
            Debug.Assert(plan != null, "Failed to produce an execution plan?");
            return plan; 
        } 

        ///  
        /// Overrides GetResultType and attempts to first retrieve the result type from the cache entry.
        /// 
        /// The query result type from this compiled query's cache entry, if possible; otherwise defers to 
        protected override TypeUsage GetResultType() 
        {
            CompiledQueryCacheEntry cacheEntry = this._cacheEntry; 
            TypeUsage resultType; 
            if (cacheEntry != null &&
                cacheEntry.TryGetResultType(out resultType)) 
            {
                return resultType;
            }
 
            return base.GetResultType();
        } 
 
        /// 
        /// Gets a LINQ expression that defines this query. 
        /// This is overridden to remove parameter references from the underlying expression,
        /// producing an expression that contains the values of those parameters as s.
        /// 
        internal override Expression Expression 
        {
            get 
            { 
                return CreateDonateableExpressionVisitor.Replace(base.Expression, ObjectContext, _rootContextParameter, _parameters);
            } 
        }

        /// 
        /// Overrides CreateExpressionConverter to return a converter that uses a binding context based on the compiled query parameters, 
        /// rather than a default binding context.
        ///  
        /// An expression converter appropriate for converting this compiled query state instance 
        protected override ExpressionConverter CreateExpressionConverter()
        { 
            // Create the command tree used for conversion and add the fixed set of parameters to it
            DbQueryCommandTree tree = new DbQueryCommandTree(this.ObjectContext.MetadataWorkspace, DataSpace.CSpace);
            foreach (var parameter in _parameters)
            { 
                parameter.CreateParameterReferenceAndAddParameterToCommandTree(tree, this.ObjectContext.Perspective);
            } 
 
            // Initialize a binding context based on the compiled query parameters.
            BindingContext context = new BindingContext(_rootContextParameter, this.ObjectContext, _parameters); 

            // Return a new expression converter that uses the initialized command tree and binding context.
            return new ExpressionConverter(this.ObjectContext, context, tree, base.Expression, this.Parameters);
        } 

        ///  
        /// Replaces ParameterExpresion with ConstantExpression 
        /// to make the expression usable as a donor expression
        ///  
        private sealed class CreateDonateableExpressionVisitor : ExpressionVisitor
        {
            private readonly Dictionary _parameterToValueLookup;
            private readonly BindingContext _bindingContext; 

            private CreateDonateableExpressionVisitor(BindingContext bindingContext, Dictionary parameterToValueLookup) 
            { 
                Debug.Assert(bindingContext != null, "bindingContext must have a value");
                Debug.Assert(parameterToValueLookup != null, "parameterToValueLookup must have a value"); 
                _bindingContext = bindingContext;
                _parameterToValueLookup = parameterToValueLookup;
            }
 
            internal static Expression Replace(Expression input, ObjectContext objectContext, ParameterExpression rootContextParameter, CompiledQueryParameter [] parameters)
            { 
                BindingContext bindingContext = new BindingContext(rootContextParameter, objectContext, parameters); 
                Dictionary parameterLookup = parameters.ToDictionary(p => p.Expression, p => p.ObjectParameter.Value);
                var replacer = new CreateDonateableExpressionVisitor(bindingContext, parameterLookup); 
                return replacer.Visit(input);
            }

            ///  
            /// Replace all the known (outer scope) Expressions with ConstantExpressions
            ///  
            /// The Expression to replace 
            /// The new constant expression
            internal override Expression Visit(Expression expression) 
            {
                object value;
                if (null != expression && _parameterToValueLookup.TryGetValue(expression, out value))
                { 
                    return Expression.Constant(value, expression.Type);
                } 
 
                return base.Visit(expression);
            } 

            internal override Expression VisitMethodCall(MethodCallExpression m)
            {
                Expression newExpression; 
                if (TryReplaceRootQuery(m, out newExpression))
                { 
                    return newExpression; 
                }
 
                return base.VisitMethodCall(m);
            }

            internal override Expression VisitMemberAccess(MemberExpression m) 
            {
                Expression newExpression; 
                if (TryReplaceRootQuery(m, out newExpression)) 
                {
                    return newExpression; 
                }
                return base.VisitMemberAccess(m);
            }
 
            bool TryReplaceRootQuery(Expression expression, out Expression newExpression)
            { 
                ObjectQuery rootQuery; 
                if (ExpressionEvaluator.TryEvaluateRootQuery(_bindingContext, expression, out rootQuery))
                { 
                    Debug.Assert(typeof(ObjectQuery).IsAssignableFrom(expression.Type), "how did we get a different type");
                    newExpression = Expression.Constant(rootQuery, expression.Type);
                    return true;
                } 

                newExpression = null; 
                return false; 
            }
        } 

        /// 
        /// Collects parameters expressions from a LINQ expression tree.
        ///  
        private sealed class ParameterExpressionVisitor : ExpressionVisitor
        { 
            internal readonly HashSet _parameters = new HashSet(); 

            private ParameterExpressionVisitor() 
            {
            }

            internal static HashSet FindAllParametersInExpression(Expression expression) 
            {
                ParameterExpressionVisitor visitor = new ParameterExpressionVisitor(); 
                visitor.Visit(expression); 
                return visitor._parameters;
            } 

            internal override Expression VisitParameter(ParameterExpression p)
            {
                _parameters.Add(p); 
                return base.VisitParameter(p);
            } 
        } 
    }
 
    internal sealed class CompiledQueryParameter
    {
        private readonly Expression _expression;
        private readonly ObjectParameter _parameter; 

        private DbParameterReferenceExpression _parameterReference; 
 
        internal CompiledQueryParameter(Expression expression, ObjectParameter parameter)
        { 
            _expression = expression;
            _parameter = parameter;
        }
 
        /// 
        /// Adds the ObjectParameter to the DbCommandTree, and creates a ParameterReferenceExpression 
        /// to be placed in the proper places in the tree 
        /// 
        /// The command tree to create operate on. 
        /// The ClrPerspective to help get the correct TypeUsage.
        internal void CreateParameterReferenceAndAddParameterToCommandTree(DbCommandTree commandTree, ClrPerspective perspective)
        {
            Debug.Assert(_parameterReference == null, "don't call this once it is already setup"); 

            TypeUsage typeUsage; 
            if(ClosureBinding.TryGetTypeUsageForObjectParameter(_parameter, perspective, out typeUsage)) 
            {
                commandTree.AddParameter(_parameter.Name, typeUsage); 
                _parameterReference = commandTree.CreateParameterReferenceExpression(_parameter.Name);
            }

            Debug.Assert(_parameterReference != null, "Not sure why this would ever happen, and it isn't handled"); 
        }
 
        internal Expression Expression { get { return _expression; } } 
        internal ObjectParameter ObjectParameter { get { return _parameter; } }
        internal DbParameterReferenceExpression ParameterReference { get { return _parameterReference; } } 
    }

}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK