CompiledELinqQueryState.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataEntity / System / Data / Objects / ELinq / CompiledELinqQueryState.cs / 1599186 / 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 System.Data.Metadata.Edm;
    using System.Linq.Expressions;
    using System.Data.Entity;
    using System.Reflection; 
    using System.Data.Common.QueryCache;
    using System.Data.Common.CommandTrees.ExpressionBuilder; 
    using System.Collections.ObjectModel; 

    ///  
    /// Models a compiled Linq to Entities ObjectQuery
    /// 
    internal sealed class CompiledELinqQueryState : ELinqQueryState
    { 
        private readonly Guid _cacheToken;
        private readonly object[] _parameterValues; 
        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 cache token to use when retrieving or storing the new instance's execution plan in the query cache 
        /// The values passed into the CompiledQuery delegate 
        internal CompiledELinqQueryState(Type elementType, ObjectContext context, LambdaExpression lambda, Guid cacheToken, object[] parameterValues)
            : base(elementType, context, lambda) 
        {
            EntityUtil.CheckArgumentNull(parameterValues, "parameterValues");

            _cacheToken = cacheToken; 
            _parameterValues = parameterValues;
 
            this.EnsureParameters(); 
            this.Parameters.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(); 
                    ReadOnlyCollection> parameters = converter.GetParameters();

                    // Prepare the execution plan using the command tree and the computed effective merge option
                    DbQueryCommandTree tree = DbQueryCommandTree.FromValidExpression(this.ObjectContext.MetadataWorkspace, DataSpace.CSpace, queryExpression); 
                    plan = ObjectQueryExecutionPlan.Prepare(this.ObjectContext, tree, this.ElementType, mergeOption, converter.PropagatedSpan, parameters);
 
                    // 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();
                    ReadOnlyCollection> parameters = converter.GetParameters(); 
                    DbQueryCommandTree tree = DbQueryCommandTree.FromValidExpression(this.ObjectContext.MetadataWorkspace, DataSpace.CSpace, 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, parameters); 

                        // 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); 
                    }
                } 
            } 

            // Get parameters from the plan and set them. 
            ObjectParameterCollection currentParams = this.EnsureParameters();
            if (plan.CompiledQueryParameters != null && plan.CompiledQueryParameters.Count > 0)
            {
                currentParams.SetReadOnly(false); 
                currentParams.Clear();
                foreach (KeyValuePair pair in plan.CompiledQueryParameters) 
                { 
                    // Parameters retrieved from the CompiledQueryParameters collection must be cloned before being added to the query.
                    // The cached plan is shared and when used in multithreaded scenarios failing to clone the parameter would result 
                    // in the code below updating the values of shared parameter instances saved in the cached plan and used by all
                    // queries using that plan, regardless of the values they were actually invoked with, causing incorrect results
                    // when those queries were later executed.
                    // 
                    ObjectParameter convertedParam = pair.Key.ShallowCopy();
                    QueryParameterExpression parameterExpression = pair.Value; 
                    currentParams.Add(convertedParam); 
                    if (parameterExpression != null)
                    { 
                        convertedParam.Value = parameterExpression.EvaluateParameter(_parameterValues);
                    }
                }
            } 
            currentParams.SetReadOnly(true);
 
            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((LambdaExpression)base.Expression, ObjectContext, _parameterValues);
            }
        }
 
        /// 
        /// 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()
        {
            LambdaExpression lambda = (LambdaExpression)base.Expression;
            Funcletizer funcletizer = Funcletizer.CreateCompiledQueryEvaluationFuncletizer(this.ObjectContext, lambda.Parameters.First(), lambda.Parameters.Skip(1).ToList().AsReadOnly()); 
            // Return a new expression converter that uses the initialized command tree and binding context.
            return new ExpressionConverter(funcletizer, lambda.Body); 
        } 

        ///  
        /// Replaces ParameterExpresion with ConstantExpression
        /// to make the expression usable as a donor expression
        /// 
        private sealed class CreateDonateableExpressionVisitor : EntityExpressionVisitor 
        {
            private readonly Dictionary _parameterToValueLookup; 
 
            private CreateDonateableExpressionVisitor(Dictionary parameterToValueLookup)
            { 
                _parameterToValueLookup = parameterToValueLookup;
            }

            internal static Expression Replace(LambdaExpression query, ObjectContext objectContext, object[] parameterValues) 
            {
                Dictionary parameterLookup = query 
                    .Parameters 
                    .Skip(1)
                    .Zip(parameterValues) 
                    .ToDictionary(pair => pair.Key, pair => pair.Value);
                parameterLookup.Add(query.Parameters.First(), objectContext);
                var replacer = new CreateDonateableExpressionVisitor(parameterLookup);
                return replacer.Visit(query.Body); 
            }
 
            internal override Expression VisitParameter(ParameterExpression p) 
            {
                object value; 
                Expression result;
                if (_parameterToValueLookup.TryGetValue(p, out value))
                {
                    result = Expression.Constant(value, p.Type); 
                }
                else 
                { 
                    result = base.VisitParameter(p);
                } 
                return result;
            }
        }
    } 
}

// 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 System.Data.Metadata.Edm;
    using System.Linq.Expressions;
    using System.Data.Entity;
    using System.Reflection; 
    using System.Data.Common.QueryCache;
    using System.Data.Common.CommandTrees.ExpressionBuilder; 
    using System.Collections.ObjectModel; 

    ///  
    /// Models a compiled Linq to Entities ObjectQuery
    /// 
    internal sealed class CompiledELinqQueryState : ELinqQueryState
    { 
        private readonly Guid _cacheToken;
        private readonly object[] _parameterValues; 
        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 cache token to use when retrieving or storing the new instance's execution plan in the query cache 
        /// The values passed into the CompiledQuery delegate 
        internal CompiledELinqQueryState(Type elementType, ObjectContext context, LambdaExpression lambda, Guid cacheToken, object[] parameterValues)
            : base(elementType, context, lambda) 
        {
            EntityUtil.CheckArgumentNull(parameterValues, "parameterValues");

            _cacheToken = cacheToken; 
            _parameterValues = parameterValues;
 
            this.EnsureParameters(); 
            this.Parameters.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(); 
                    ReadOnlyCollection> parameters = converter.GetParameters();

                    // Prepare the execution plan using the command tree and the computed effective merge option
                    DbQueryCommandTree tree = DbQueryCommandTree.FromValidExpression(this.ObjectContext.MetadataWorkspace, DataSpace.CSpace, queryExpression); 
                    plan = ObjectQueryExecutionPlan.Prepare(this.ObjectContext, tree, this.ElementType, mergeOption, converter.PropagatedSpan, parameters);
 
                    // 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();
                    ReadOnlyCollection> parameters = converter.GetParameters(); 
                    DbQueryCommandTree tree = DbQueryCommandTree.FromValidExpression(this.ObjectContext.MetadataWorkspace, DataSpace.CSpace, 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, parameters); 

                        // 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); 
                    }
                } 
            } 

            // Get parameters from the plan and set them. 
            ObjectParameterCollection currentParams = this.EnsureParameters();
            if (plan.CompiledQueryParameters != null && plan.CompiledQueryParameters.Count > 0)
            {
                currentParams.SetReadOnly(false); 
                currentParams.Clear();
                foreach (KeyValuePair pair in plan.CompiledQueryParameters) 
                { 
                    // Parameters retrieved from the CompiledQueryParameters collection must be cloned before being added to the query.
                    // The cached plan is shared and when used in multithreaded scenarios failing to clone the parameter would result 
                    // in the code below updating the values of shared parameter instances saved in the cached plan and used by all
                    // queries using that plan, regardless of the values they were actually invoked with, causing incorrect results
                    // when those queries were later executed.
                    // 
                    ObjectParameter convertedParam = pair.Key.ShallowCopy();
                    QueryParameterExpression parameterExpression = pair.Value; 
                    currentParams.Add(convertedParam); 
                    if (parameterExpression != null)
                    { 
                        convertedParam.Value = parameterExpression.EvaluateParameter(_parameterValues);
                    }
                }
            } 
            currentParams.SetReadOnly(true);
 
            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((LambdaExpression)base.Expression, ObjectContext, _parameterValues);
            }
        }
 
        /// 
        /// 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()
        {
            LambdaExpression lambda = (LambdaExpression)base.Expression;
            Funcletizer funcletizer = Funcletizer.CreateCompiledQueryEvaluationFuncletizer(this.ObjectContext, lambda.Parameters.First(), lambda.Parameters.Skip(1).ToList().AsReadOnly()); 
            // Return a new expression converter that uses the initialized command tree and binding context.
            return new ExpressionConverter(funcletizer, lambda.Body); 
        } 

        ///  
        /// Replaces ParameterExpresion with ConstantExpression
        /// to make the expression usable as a donor expression
        /// 
        private sealed class CreateDonateableExpressionVisitor : EntityExpressionVisitor 
        {
            private readonly Dictionary _parameterToValueLookup; 
 
            private CreateDonateableExpressionVisitor(Dictionary parameterToValueLookup)
            { 
                _parameterToValueLookup = parameterToValueLookup;
            }

            internal static Expression Replace(LambdaExpression query, ObjectContext objectContext, object[] parameterValues) 
            {
                Dictionary parameterLookup = query 
                    .Parameters 
                    .Skip(1)
                    .Zip(parameterValues) 
                    .ToDictionary(pair => pair.Key, pair => pair.Value);
                parameterLookup.Add(query.Parameters.First(), objectContext);
                var replacer = new CreateDonateableExpressionVisitor(parameterLookup);
                return replacer.Visit(query.Body); 
            }
 
            internal override Expression VisitParameter(ParameterExpression p) 
            {
                object value; 
                Expression result;
                if (_parameterToValueLookup.TryGetValue(p, out value))
                {
                    result = Expression.Constant(value, p.Type); 
                }
                else 
                { 
                    result = base.VisitParameter(p);
                } 
                return result;
            }
        }
    } 
}

// 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