Example #1
0
        private DbQueryCommandTree Simplify(DbQueryCommandTree view)
        {
            DbExpression query = PatternMatchRuleProcessor.Create(PatternMatchRule.Create(ViewSimplifier._patternCollapseNestedProjection, new Func <DbExpression, DbExpression>(ViewSimplifier.CollapseNestedProjection)), PatternMatchRule.Create(ViewSimplifier._patternCase, new Func <DbExpression, DbExpression>(ViewSimplifier.SimplifyCaseStatement)), PatternMatchRule.Create(ViewSimplifier._patternNestedTphDiscriminator, new Func <DbExpression, DbExpression>(ViewSimplifier.SimplifyNestedTphDiscriminator)), PatternMatchRule.Create(ViewSimplifier._patternEntityConstructor, new Func <DbExpression, DbExpression>(this.AddFkRelatedEntityRefs)))(view.Query);

            view = DbQueryCommandTree.FromValidExpression(view.MetadataWorkspace, view.DataSpace, query, view.UseDatabaseNullSemantics);
            return(view);
        }
Example #2
0
        internal DbQueryCommandTree GenerateFunctionView(out DiscriminatorMap discriminatorMap)
        {
            DebugCheck.NotNull(m_mappingItemCollection);

            discriminatorMap = null;

            // Prepare the direct call of the store function as StoreFunction(@EdmFunc_p1, ..., @EdmFunc_pN).
            // Note that function call arguments are command parameters created from the m_edmFunction parameters.
            Debug.Assert(TargetFunction != null, "this.TargetFunction != null");
            DbExpression storeFunctionInvoke = TargetFunction.Invoke(GetParametersForTargetFunctionCall());

            // Generate the query expression producing c-space result from s-space function call(s).
            DbExpression queryExpression;

            if (m_structuralTypeMappings != null)
            {
                queryExpression = GenerateStructuralTypeResultMappingView(storeFunctionInvoke, out discriminatorMap);
                Debug.Assert(
                    queryExpression != null &&
                    TypeSemantics.IsPromotableTo(queryExpression.ResultType, FunctionImport.ReturnParameter.TypeUsage),
                    "TypeSemantics.IsPromotableTo(queryExpression.ResultType, this.FunctionImport.ReturnParameter.TypeUsage)");
            }
            else
            {
                queryExpression = GenerateScalarResultMappingView(storeFunctionInvoke);
                Debug.Assert(
                    queryExpression != null &&
                    TypeSemantics.IsEqual(queryExpression.ResultType, FunctionImport.ReturnParameter.TypeUsage),
                    "TypeSemantics.IsEqual(queryExpression.ResultType, this.FunctionImport.ReturnParameter.TypeUsage)");
            }

            // Generate parameterized command, where command parameters are semantically the c-space function parameters.
            return(DbQueryCommandTree.FromValidExpression(
                       m_mappingItemCollection.Workspace, TargetPerspective.TargetPerspectiveDataSpace, queryExpression));
        }
        public virtual ObjectQueryExecutionPlan Prepare(
            ObjectContext context, DbQueryCommandTree tree, Type elementType, MergeOption mergeOption, Span span,
            IEnumerable <Tuple <ObjectParameter, QueryParameterExpression> > compiledQueryParameters, AliasGenerator aliasGenerator)
        {
            var treeResultType = tree.Query.ResultType;

            // Rewrite this tree for Span?
            DbExpression spannedQuery = null;
            SpanIndex    spanInfo;

            if (ObjectSpanRewriter.TryRewrite(tree, span, mergeOption, aliasGenerator, out spannedQuery, out spanInfo))
            {
                tree = DbQueryCommandTree.FromValidExpression(tree.MetadataWorkspace, tree.DataSpace, spannedQuery);
            }
            else
            {
                spanInfo = null;
            }

            var entityDefinition = CreateCommandDefinition(context, tree);

            var cacheManager = context.Perspective.MetadataWorkspace.GetQueryCacheManager();

            var shaperFactory = Translator.TranslateColumnMap(
                _translator,
                elementType, cacheManager, entityDefinition.CreateColumnMap(null),
                context.MetadataWorkspace, spanInfo, mergeOption, false);

            // attempt to determine entity information for this query (e.g. which entity type and which entity set)

            EntitySet singleEntitySet = null;

            // determine if the entity set is unambiguous given the entity type
            if (treeResultType.EdmType.BuiltInTypeKind == BuiltInTypeKind.CollectionType &&
                entityDefinition.EntitySets != null)
            {
                foreach (var entitySet in entityDefinition.EntitySets)
                {
                    if (entitySet != null &&
                        entitySet.ElementType.IsAssignableFrom(((CollectionType)treeResultType.EdmType).TypeUsage.EdmType))
                    {
                        if (singleEntitySet == null)
                        {
                            // found a single match
                            singleEntitySet = entitySet;
                        }
                        else
                        {
                            // there's more than one matching entity set
                            singleEntitySet = null;
                            break;
                        }
                    }
                }
            }

            return(new ObjectQueryExecutionPlan(
                       entityDefinition, shaperFactory, treeResultType, mergeOption, singleEntitySet, compiledQueryParameters));
        }
Example #4
0
        /// <summary>
        /// The only entry point.
        /// Rewrites the given tree by replacing expressions that are not directly supported on SQL Server 2000
        /// with alterntive translations.
        /// </summary>
        /// <param name="originalTree">The tree to rewrite</param>
        /// <returns>The new tree</returns>
        internal static DbQueryCommandTree Rewrite(DbQueryCommandTree originalTree)
        {
            Debug.Assert(originalTree != null, "OriginalTree is null");
            Sql8ExpressionRewriter rewriter = new Sql8ExpressionRewriter(originalTree.MetadataWorkspace);
            DbExpression           newQuery = rewriter.VisitExpression(originalTree.Query);

            return(DbQueryCommandTree.FromValidExpression(originalTree.MetadataWorkspace, originalTree.DataSpace, newQuery));
        }
Example #5
0
        internal DbQueryCommandTree GenerateFunctionView(
            out DiscriminatorMap discriminatorMap)
        {
            discriminatorMap = (DiscriminatorMap)null;
            DbExpression storeFunctionInvoke = (DbExpression)this.TargetFunction.Invoke(this.GetParametersForTargetFunctionCall());

            return(DbQueryCommandTree.FromValidExpression(this._containerMapping.StorageMappingItemCollection.Workspace, DataSpace.SSpace, this.m_structuralTypeMappings == null ? this.GenerateScalarResultMappingView(storeFunctionInvoke) : this.GenerateStructuralTypeResultMappingView(storeFunctionInvoke, out discriminatorMap), true));
        }
Example #6
0
        /// <summary>
        /// Returns Cqtl query that represents a query/update mapping view for the view information that was supplied in the constructor.
        /// </summary>
        internal DbQueryCommandTree GenerateCqt()
        {
            // Generate a CqlBlock tree and then convert that to CQT.
            CqlBlock blockTree = GenerateCqlBlockTree();

            DbExpression query = blockTree.AsCqt(true);

            Debug.Assert(query != null, "Null CQT generated for query/update view.");

            return(DbQueryCommandTree.FromValidExpression(m_mappingItemCollection.Workspace, TargetPerspective.TargetPerspectiveDataSpace, query));
        }
Example #7
0
        // <summary>
        // Returns Cqtl query that represents a query/update mapping view for the view information that was supplied in the constructor.
        // </summary>
        internal DbQueryCommandTree GenerateCqt()
        {
            // Generate a CqlBlock tree and then convert that to CQT.
            var blockTree = GenerateCqlBlockTree();

            var query = blockTree.AsCqt(true);

            Debug.Assert(query != null, "Null CQT generated for query/update view.");

            return(DbQueryCommandTree.FromValidExpression(
                       m_mappingItemCollection.Workspace, TargetPerspective.TargetPerspectiveDataSpace, query,
                       useDatabaseNullSemantics: true, disableFilterOverProjectionSimplificationForCustomFunctions: false));
        }
        public virtual ObjectQueryExecutionPlan Prepare(
            ObjectContext context,
            DbQueryCommandTree tree,
            Type elementType,
            MergeOption mergeOption,
            bool streaming,
            Span span,
            IEnumerable <Tuple <ObjectParameter, QueryParameterExpression> > compiledQueryParameters,
            AliasGenerator aliasGenerator)
        {
            TypeUsage    resultType = tree.Query.ResultType;
            DbExpression newQuery;
            SpanIndex    spanInfo;

            if (ObjectSpanRewriter.TryRewrite(tree, span, mergeOption, aliasGenerator, out newQuery, out spanInfo))
            {
                tree = DbQueryCommandTree.FromValidExpression(tree.MetadataWorkspace, tree.DataSpace, newQuery, tree.UseDatabaseNullSemantics);
            }
            else
            {
                spanInfo = (SpanIndex)null;
            }
            EntityCommandDefinition commandDefinition   = ObjectQueryExecutionPlanFactory.CreateCommandDefinition(context, tree);
            ShaperFactory           resultShaperFactory = System.Data.Entity.Core.Common.Internal.Materialization.Translator.TranslateColumnMap(this._translator, elementType, commandDefinition.CreateColumnMap((DbDataReader)null), context.MetadataWorkspace, spanInfo, mergeOption, streaming, false);
            EntitySet singleEntitySet = (EntitySet)null;

            if (resultType.EdmType.BuiltInTypeKind == BuiltInTypeKind.CollectionType && commandDefinition.EntitySets != null)
            {
                foreach (EntitySet entitySet in commandDefinition.EntitySets)
                {
                    if (entitySet != null && entitySet.ElementType.IsAssignableFrom(((CollectionType)resultType.EdmType).TypeUsage.EdmType))
                    {
                        if (singleEntitySet == null)
                        {
                            singleEntitySet = entitySet;
                        }
                        else
                        {
                            singleEntitySet = (EntitySet)null;
                            break;
                        }
                    }
                }
            }
            return(new ObjectQueryExecutionPlan((DbCommandDefinition)commandDefinition, resultShaperFactory, resultType, mergeOption, streaming, singleEntitySet, compiledQueryParameters));
        }
Example #9
0
        private DbQueryCommandTree Simplify(DbQueryCommandTree view)
        {
            var simplifier = PatternMatchRuleProcessor.Create(
                // determines if an expression is of the form outerProject(outerProjection(innerProject(innerNew)))
                PatternMatchRule.Create(_patternCollapseNestedProjection, CollapseNestedProjection),
                // A case statement can potentially be simplified
                PatternMatchRule.Create(_patternCase, SimplifyCaseStatement),
                // Nested TPH discriminator pattern can be converted to the expected TPH discriminator pattern
                PatternMatchRule.Create(_patternNestedTphDiscriminator, SimplifyNestedTphDiscriminator),
                // Entity constructors may be augmented with FK-based related entity refs
                PatternMatchRule.Create(_patternEntityConstructor, AddFkRelatedEntityRefs)
                );

            var queryExpression = view.Query;

            queryExpression = simplifier(queryExpression);

            view = DbQueryCommandTree.FromValidExpression(view.MetadataWorkspace, view.DataSpace, queryExpression);
            return(view);
        }
Example #10
0
        internal override ObjectQueryExecutionPlan GetExecutionPlan(MergeOption?forMergeOption)
        {
            Debug.Assert(Span == null, "Include span specified on compiled LINQ-based ObjectQuery instead of within the expression tree?");

            // If this query has already been prepared, its current execution plan may no longer be valid.
            var plan = _cachedPlan;

            if (plan != null)
            {
                // Was a merge option specified in the call to Execute(MergeOption) or set via ObjectQuery.MergeOption?
                var explicitMergeOption = GetMergeOption(forMergeOption, UserSpecifiedMergeOption);

                // If a merge option was explicitly specified, and it does not match the plan's merge option, then the plan is no longer valid.
                // If the context flag UseCSharpNullComparisonBehavior was modified, then the plan is no longer valid.
                if ((explicitMergeOption.HasValue &&
                     explicitMergeOption.Value != plan.MergeOption)
                    ||
                    _recompileRequired()
                    ||
                    ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior != _useCSharpNullComparisonBehavior)
                {
                    plan = null;
                }
            }

            // The plan may have been invalidated above, or this query may never have been prepared.
            if (plan == null)
            {
                // Metadata is required to generate the execution plan.
                ObjectContext.EnsureMetadata();

                // Reset internal state
                _recompileRequired = null;
                ResetParameters();

                // Translate LINQ expression to a DbExpression
                var converter       = CreateExpressionConverter();
                var queryExpression = converter.Convert();

                // This delegate tells us when a part of the expression tree has changed requiring a recompile.
                _recompileRequired = converter.RecompileRequired;

                // Determine the merge option, with the following precedence:
                // 1. A merge option was specified explicitly as the argument to Execute(MergeOption).
                // 2. The user has set the MergeOption property on the ObjectQuery instance.
                // 3. A merge option has been extracted from the 'root' query and propagated to the root of the expression tree.
                // 4. The global default merge option.
                var mergeOption = EnsureMergeOption(
                    forMergeOption,
                    UserSpecifiedMergeOption,
                    converter.PropagatedMergeOption);

                _useCSharpNullComparisonBehavior = ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior;

                // If parameters were aggregated from referenced (non-LINQ) ObjectQuery instances then add them to the parameters collection
                _linqParameters = converter.GetParameters();
                if (_linqParameters != null &&
                    _linqParameters.Any())
                {
                    var currentParams = EnsureParameters();
                    currentParams.SetReadOnly(false);
                    foreach (var pair in _linqParameters)
                    {
                        // Note that it is safe to add the parameter directly only
                        // because parameters are cloned before they are added to the
                        // converter's parameter collection, or they came from this
                        // instance's parameter collection in the first place.
                        var convertedParam = pair.Item1;
                        currentParams.Add(convertedParam);
                    }
                    currentParams.SetReadOnly(true);
                }

                // Try retrieving the execution plan from the global query cache (if plan caching is enabled).
                QueryCacheManager cacheManager = null;
                LinqQueryCacheKey cacheKey     = null;
                if (PlanCachingEnabled && !_recompileRequired())
                {
                    // Create a new cache key that reflects the current state of the Parameters collection
                    // and the Span object (if any), and uses the specified merge option.
                    string expressionKey;
                    if (ExpressionKeyGen.TryGenerateKey(queryExpression, out expressionKey))
                    {
                        cacheKey = new LinqQueryCacheKey(
                            expressionKey,
                            (null == Parameters ? 0 : Parameters.Count),
                            (null == Parameters ? null : Parameters.GetCacheKey()),
                            (null == converter.PropagatedSpan ? null : converter.PropagatedSpan.GetCacheKey()),
                            mergeOption,
                            _useCSharpNullComparisonBehavior,
                            ElementType);

                        cacheManager = ObjectContext.MetadataWorkspace.GetQueryCacheManager();
                        ObjectQueryExecutionPlan executionPlan = null;
                        if (cacheManager.TryCacheLookup(cacheKey, out executionPlan))
                        {
                            plan = executionPlan;
                        }
                    }
                }

                // If execution plan wasn't retrieved from the cache, build a new one and cache it.
                if (plan == null)
                {
                    var tree = DbQueryCommandTree.FromValidExpression(ObjectContext.MetadataWorkspace, DataSpace.CSpace, queryExpression);
                    plan = _objectQueryExecutionPlanFactory.Prepare(
                        ObjectContext, tree, ElementType, mergeOption, converter.PropagatedSpan, null, converter.AliasGenerator);

                    // If caching is enabled then update the cache now.
                    // Note: the logic is the same as in EntitySqlQueryState.
                    if (cacheKey != null)
                    {
                        var             newEntry   = new QueryCacheEntry(cacheKey, plan);
                        QueryCacheEntry foundEntry = null;
                        if (cacheManager.TryLookupAndAdd(newEntry, out foundEntry))
                        {
                            // If TryLookupAndAdd returns 'true' then the entry was already present in the cache when the attempt to add was made.
                            // In this case the existing execution plan should be used.
                            plan = (ObjectQueryExecutionPlan)foundEntry.GetTarget();
                        }
                    }
                }

                // Remember the current plan in the local cache, so that we don't have to recalc the key and look into the global cache
                // if the same instance of query gets executed more than once.
                _cachedPlan = plan;
            }

            // Evaluate parameter values for the query.
            if (_linqParameters != null)
            {
                foreach (var pair in _linqParameters)
                {
                    var parameter           = pair.Item1;
                    var parameterExpression = pair.Item2;
                    if (null != parameterExpression)
                    {
                        parameter.Value = parameterExpression.EvaluateParameter(null);
                    }
                }
            }

            return(plan);
        }
        internal override ObjectQueryExecutionPlan GetExecutionPlan(
            MergeOption?forMergeOption)
        {
            ObjectQueryExecutionPlan queryExecutionPlan1 = this._cachedPlan;

            if (queryExecutionPlan1 != null)
            {
                MergeOption?mergeOption = ObjectQueryState.GetMergeOption(forMergeOption, this.UserSpecifiedMergeOption);
                if (mergeOption.HasValue && mergeOption.Value != queryExecutionPlan1.MergeOption || (this._recompileRequired() || this.ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior != this._useCSharpNullComparisonBehavior))
                {
                    queryExecutionPlan1 = (ObjectQueryExecutionPlan)null;
                }
            }
            if (queryExecutionPlan1 == null)
            {
                this._recompileRequired = (Func <bool>)null;
                this.ResetParameters();
                ExpressionConverter expressionConverter = this.CreateExpressionConverter();
                DbExpression        dbExpression        = expressionConverter.Convert();
                this._recompileRequired = expressionConverter.RecompileRequired;
                MergeOption mergeOption = ObjectQueryState.EnsureMergeOption(forMergeOption, this.UserSpecifiedMergeOption, expressionConverter.PropagatedMergeOption);
                this._useCSharpNullComparisonBehavior = this.ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior;
                this._linqParameters = expressionConverter.GetParameters();
                if (this._linqParameters != null && this._linqParameters.Any <Tuple <ObjectParameter, QueryParameterExpression> >())
                {
                    ObjectParameterCollection parameterCollection = this.EnsureParameters();
                    parameterCollection.SetReadOnly(false);
                    foreach (Tuple <ObjectParameter, QueryParameterExpression> linqParameter in this._linqParameters)
                    {
                        ObjectParameter objectParameter = linqParameter.Item1;
                        parameterCollection.Add(objectParameter);
                    }
                    parameterCollection.SetReadOnly(true);
                }
                QueryCacheManager queryCacheManager = (QueryCacheManager)null;
                LinqQueryCacheKey key1 = (LinqQueryCacheKey)null;
                string            key2;
                if (this.PlanCachingEnabled && !this._recompileRequired() && ExpressionKeyGen.TryGenerateKey(dbExpression, out key2))
                {
                    key1 = new LinqQueryCacheKey(key2, this.Parameters == null ? 0 : this.Parameters.Count, this.Parameters == null ? (string)null : this.Parameters.GetCacheKey(), expressionConverter.PropagatedSpan == null ? (string)null : expressionConverter.PropagatedSpan.GetCacheKey(), mergeOption, this.EffectiveStreamingBehavior, this._useCSharpNullComparisonBehavior, this.ElementType);
                    queryCacheManager = this.ObjectContext.MetadataWorkspace.GetQueryCacheManager();
                    ObjectQueryExecutionPlan queryExecutionPlan2 = (ObjectQueryExecutionPlan)null;
                    if (queryCacheManager.TryCacheLookup <LinqQueryCacheKey, ObjectQueryExecutionPlan>(key1, out queryExecutionPlan2))
                    {
                        queryExecutionPlan1 = queryExecutionPlan2;
                    }
                }
                if (queryExecutionPlan1 == null)
                {
                    queryExecutionPlan1 = this._objectQueryExecutionPlanFactory.Prepare(this.ObjectContext, DbQueryCommandTree.FromValidExpression(this.ObjectContext.MetadataWorkspace, DataSpace.CSpace, dbExpression, !this._useCSharpNullComparisonBehavior), this.ElementType, mergeOption, this.EffectiveStreamingBehavior, expressionConverter.PropagatedSpan, (IEnumerable <Tuple <ObjectParameter, QueryParameterExpression> >)null, expressionConverter.AliasGenerator);
                    if (key1 != null)
                    {
                        QueryCacheEntry inQueryCacheEntry  = new QueryCacheEntry((QueryCacheKey)key1, (object)queryExecutionPlan1);
                        QueryCacheEntry outQueryCacheEntry = (QueryCacheEntry)null;
                        if (queryCacheManager.TryLookupAndAdd(inQueryCacheEntry, out outQueryCacheEntry))
                        {
                            queryExecutionPlan1 = (ObjectQueryExecutionPlan)outQueryCacheEntry.GetTarget();
                        }
                    }
                }
                this._cachedPlan = queryExecutionPlan1;
            }
            if (this._linqParameters != null)
            {
                foreach (Tuple <ObjectParameter, QueryParameterExpression> linqParameter in this._linqParameters)
                {
                    ObjectParameter          objectParameter     = linqParameter.Item1;
                    QueryParameterExpression parameterExpression = linqParameter.Item2;
                    if (parameterExpression != null)
                    {
                        objectParameter.Value = parameterExpression.EvaluateParameter((object[])null);
                    }
                }
            }
            return(queryExecutionPlan1);
        }
Example #12
0
 internal DbQueryCommandTree GenerateCqt()
 {
     return(DbQueryCommandTree.FromValidExpression(this.m_mappingItemCollection.Workspace, DataSpace.SSpace, this.GenerateCqlBlockTree().AsCqt(true), true));
 }
        internal override ObjectQueryExecutionPlan GetExecutionPlan(MergeOption?forMergeOption)
        {
            Debug.Assert(Span == null, "Include span specified on compiled LINQ-based ObjectQuery instead of within the expression tree?");
            Debug.Assert(_cachedPlan == null, "Cached plan should not be set on compiled LINQ queries");

            ObjectQueryExecutionPlan plan = null;
            var cacheEntry = _cacheEntry;
            var useCSharpNullComparisonBehavior = ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior;
            var disableFilterOverProjectionSimplificationForCustomFunctions = ObjectContext.ContextOptions.DisableFilterOverProjectionSimplificationForCustomFunctions;

            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.
                var mergeOption = EnsureMergeOption(forMergeOption, UserSpecifiedMergeOption, cacheEntry.PropagatedMergeOption);

                // Ask for the corresponding execution plan
                plan = cacheEntry.GetExecutionPlan(mergeOption, useCSharpNullComparisonBehavior);
                if (plan == null)
                {
                    // Convert the LINQ expression to produce a command tree
                    var converter       = CreateExpressionConverter();
                    var queryExpression = converter.Convert();
                    var parameters      = converter.GetParameters();

                    // Prepare the execution plan using the command tree and the computed effective merge option
                    var tree = DbQueryCommandTree.FromValidExpression(
                        ObjectContext.MetadataWorkspace, DataSpace.CSpace, queryExpression, !useCSharpNullComparisonBehavior, disableFilterOverProjectionSimplificationForCustomFunctions);
                    plan = _objectQueryExecutionPlanFactory.Prepare(
                        ObjectContext, tree, ElementType, mergeOption, EffectiveStreamingBehavior, converter.PropagatedSpan, parameters,
                        converter.AliasGenerator);

                    // Update and retrieve the execution plan
                    plan = cacheEntry.SetExecutionPlan(plan, useCSharpNullComparisonBehavior);
                }
            }
            else
            {
                // This instance does not yet have a reference to a cache entry.
                // First, attempt to retrieve an existing cache entry.
                var cacheManager = ObjectContext.MetadataWorkspace.GetQueryCacheManager();
                var cacheKey     = new CompiledQueryCacheKey(_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 use the UseCSharpNullComparisonBehavior flag to retrieve the corresponding execution plan.
                    _cacheEntry = cacheEntry;
                    var mergeOption = EnsureMergeOption(forMergeOption, UserSpecifiedMergeOption, cacheEntry.PropagatedMergeOption);
                    plan = cacheEntry.GetExecutionPlan(mergeOption, useCSharpNullComparisonBehavior);
                }

                // 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.
                    var converter       = CreateExpressionConverter();
                    var queryExpression = converter.Convert();
                    var parameters      = converter.GetParameters();
                    var tree            = DbQueryCommandTree.FromValidExpression(
                        ObjectContext.MetadataWorkspace, DataSpace.CSpace, queryExpression, !useCSharpNullComparisonBehavior, disableFilterOverProjectionSimplificationForCustomFunctions);

                    // 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.
                        _cacheEntry = cacheEntry;
                    }

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

                    // Ask the (retrieved or constructed) cache entry for the corresponding execution plan.
                    plan = cacheEntry.GetExecutionPlan(mergeOption, useCSharpNullComparisonBehavior);
                    if (plan == null)
                    {
                        // The plan is not present, so prepare it now using the computed effective merge option
                        plan = _objectQueryExecutionPlanFactory.Prepare(
                            ObjectContext, tree, ElementType, mergeOption, EffectiveStreamingBehavior, converter.PropagatedSpan, parameters,
                            converter.AliasGenerator);

                        // Update the execution plan 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, useCSharpNullComparisonBehavior);
                    }
                }
            }

            // Get parameters from the plan and set them.
            var currentParams = EnsureParameters();

            if (plan.CompiledQueryParameters != null &&
                plan.CompiledQueryParameters.Any())
            {
                currentParams.SetReadOnly(false);
                currentParams.Clear();
                foreach (var 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.
                    //
                    var convertedParam      = pair.Item1.ShallowCopy();
                    var parameterExpression = pair.Item2;
                    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);
        }
        internal override ObjectQueryExecutionPlan GetExecutionPlan(
            MergeOption?forMergeOption)
        {
            MergeOption mergeOption = ObjectQueryState.EnsureMergeOption(forMergeOption, this.UserSpecifiedMergeOption);
            ObjectQueryExecutionPlan queryExecutionPlan1 = this._cachedPlan;

            if (queryExecutionPlan1 != null)
            {
                if (queryExecutionPlan1.MergeOption == mergeOption && queryExecutionPlan1.Streaming == this.EffectiveStreamingBehavior)
                {
                    return(queryExecutionPlan1);
                }
                queryExecutionPlan1 = (ObjectQueryExecutionPlan)null;
            }
            QueryCacheManager      queryCacheManager = (QueryCacheManager)null;
            EntitySqlQueryCacheKey key = (EntitySqlQueryCacheKey)null;

            if (this.PlanCachingEnabled)
            {
                key = new EntitySqlQueryCacheKey(this.ObjectContext.DefaultContainerName, this._queryText, this.Parameters == null ? 0 : this.Parameters.Count, this.Parameters == null ? (string)null : this.Parameters.GetCacheKey(), this.Span == null ? (string)null : this.Span.GetCacheKey(), mergeOption, this.EffectiveStreamingBehavior, this.ElementType);
                queryCacheManager = this.ObjectContext.MetadataWorkspace.GetQueryCacheManager();
                ObjectQueryExecutionPlan queryExecutionPlan2 = (ObjectQueryExecutionPlan)null;
                if (queryCacheManager.TryCacheLookup <EntitySqlQueryCacheKey, ObjectQueryExecutionPlan>(key, out queryExecutionPlan2))
                {
                    queryExecutionPlan1 = queryExecutionPlan2;
                }
            }
            if (queryExecutionPlan1 == null)
            {
                queryExecutionPlan1 = this._objectQueryExecutionPlanFactory.Prepare(this.ObjectContext, DbQueryCommandTree.FromValidExpression(this.ObjectContext.MetadataWorkspace, DataSpace.CSpace, this.Parse(), true), this.ElementType, mergeOption, this.EffectiveStreamingBehavior, this.Span, (IEnumerable <Tuple <ObjectParameter, QueryParameterExpression> >)null, DbExpressionBuilder.AliasGenerator);
                if (key != null)
                {
                    QueryCacheEntry inQueryCacheEntry  = new QueryCacheEntry((QueryCacheKey)key, (object)queryExecutionPlan1);
                    QueryCacheEntry outQueryCacheEntry = (QueryCacheEntry)null;
                    if (queryCacheManager.TryLookupAndAdd(inQueryCacheEntry, out outQueryCacheEntry))
                    {
                        queryExecutionPlan1 = (ObjectQueryExecutionPlan)outQueryCacheEntry.GetTarget();
                    }
                }
            }
            if (this.Parameters != null)
            {
                this.Parameters.SetReadOnly(true);
            }
            this._cachedPlan = queryExecutionPlan1;
            return(queryExecutionPlan1);
        }
Example #15
0
        internal override ObjectQueryExecutionPlan GetExecutionPlan(MergeOption?forMergeOption)
        {
            // Determine the required merge option, with the following precedence:
            // 1. The merge option specified to Execute(MergeOption) as forMergeOption.
            // 2. The merge option set via ObjectQuery.MergeOption.
            // 3. The global default merge option.
            var mergeOption = EnsureMergeOption(forMergeOption, UserSpecifiedMergeOption);

            // If a cached plan is present, then it can be reused if it has the required merge option and streaming behavior
            // (since span and parameters cannot change between executions). However, if the cached
            // plan does not have the required merge option we proceed as if it were not present.
            var plan = _cachedPlan;

            if (plan != null)
            {
                if (plan.MergeOption == mergeOption &&
                    plan.Streaming == EffectiveStreamingBehavior)
                {
                    return(plan);
                }
                else
                {
                    plan = null;
                }
            }

            // There is no cached plan (or it was cleared), so the execution plan must be retrieved from
            // the global query cache (if plan caching is enabled) or rebuilt for the required merge option.
            QueryCacheManager      cacheManager = null;
            EntitySqlQueryCacheKey cacheKey     = null;

            if (PlanCachingEnabled)
            {
                // Create a new cache key that reflects the current state of the Parameters collection
                // and the Span object (if any), and uses the specified merge option.
                cacheKey = new EntitySqlQueryCacheKey(
                    ObjectContext.DefaultContainerName,
                    _queryText,
                    (null == Parameters ? 0 : Parameters.Count),
                    (null == Parameters ? null : Parameters.GetCacheKey()),
                    (null == Span ? null : Span.GetCacheKey()),
                    mergeOption,
                    EffectiveStreamingBehavior,
                    ElementType);

                cacheManager = ObjectContext.MetadataWorkspace.GetQueryCacheManager();
                ObjectQueryExecutionPlan executionPlan = null;
                if (cacheManager.TryCacheLookup(cacheKey, out executionPlan))
                {
                    plan = executionPlan;
                }
            }

            if (plan == null)
            {
                // Either caching is not enabled or the execution plan was not found in the cache
                var queryExpression = Parse();
                Debug.Assert(queryExpression != null, "EntitySqlQueryState.Parse returned null expression?");
                var tree = DbQueryCommandTree.FromValidExpression(
                    ObjectContext.MetadataWorkspace, DataSpace.CSpace, queryExpression,
                    useDatabaseNullSemantics: true);
                plan = _objectQueryExecutionPlanFactory.Prepare(
                    ObjectContext, tree, ElementType, mergeOption, EffectiveStreamingBehavior, Span, null,
                    DbExpressionBuilder.AliasGenerator);

                // If caching is enabled then update the cache now.
                // Note: the logic is the same as in ELinqQueryState.
                if (cacheKey != null)
                {
                    var             newEntry   = new QueryCacheEntry(cacheKey, plan);
                    QueryCacheEntry foundEntry = null;
                    if (cacheManager.TryLookupAndAdd(newEntry, out foundEntry))
                    {
                        // If TryLookupAndAdd returns 'true' then the entry was already present in the cache when the attempt to add was made.
                        // In this case the existing execution plan should be used.
                        plan = (ObjectQueryExecutionPlan)foundEntry.GetTarget();
                    }
                }
            }

            if (Parameters != null)
            {
                Parameters.SetReadOnly(true);
            }

            // Update the cached plan with the newly retrieved/prepared plan
            _cachedPlan = plan;

            // Return the execution plan
            return(plan);
        }
        internal static ObjectQueryExecutionPlan Prepare(ObjectContext context, DbQueryCommandTree tree, Type elementType, MergeOption mergeOption, Span span, CompiledQueryParameters compiledQueryParameters, AliasGenerator aliasGenerator)
        {
            TypeUsage treeResultType = tree.Query.ResultType;

            // Rewrite this tree for Span?
            DbExpression spannedQuery = null;
            SpanIndex    spanInfo;

            if (ObjectSpanRewriter.TryRewrite(tree, span, mergeOption, aliasGenerator, out spannedQuery, out spanInfo))
            {
                tree = DbQueryCommandTree.FromValidExpression(tree.MetadataWorkspace, tree.DataSpace, spannedQuery);
            }
            else
            {
                spanInfo = null;
            }

            DbConnection        connection = context.Connection;
            DbCommandDefinition definition = null;

            // The connection is required to get to the CommandDefinition builder.
            if (connection == null)
            {
                throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.ObjectQuery_InvalidConnection);
            }

            DbProviderServices services = DbProviderServices.GetProviderServices(connection);

            try
            {
                definition = services.CreateCommandDefinition(tree);
            }
            catch (EntityCommandCompilationException)
            {
                // If we're running against EntityCommand, we probably already caught the providers'
                // exception and wrapped it, we don't want to do that again, so we'll just rethrow
                // here instead.
                throw;
            }
            catch (Exception e)
            {
                // we should not be wrapping all exceptions
                if (EntityUtil.IsCatchableExceptionType(e))
                {
                    // we don't wan't folks to have to know all the various types of exceptions that can
                    // occur, so we just rethrow a CommandDefinitionException and make whatever we caught
                    // the inner exception of it.
                    throw EntityUtil.CommandCompilation(System.Data.Entity.Strings.EntityClient_CommandDefinitionPreparationFailed, e);
                }
                throw;
            }

            if (definition == null)
            {
                throw EntityUtil.ProviderDoesNotSupportCommandTrees();
            }

            EntityCommandDefinition entityDefinition = (EntityCommandDefinition)definition;
            QueryCacheManager       cacheManager     = context.Perspective.MetadataWorkspace.GetQueryCacheManager();

            ShaperFactory shaperFactory = ShaperFactory.Create(elementType, cacheManager, entityDefinition.CreateColumnMap(null),
                                                               context.MetadataWorkspace, spanInfo, mergeOption, false);

            // attempt to determine entity information for this query (e.g. which entity type and which entity set)
            //EntityType rootEntityType = null;

            EntitySet singleEntitySet = null;

            if (treeResultType.EdmType.BuiltInTypeKind == BuiltInTypeKind.CollectionType)
            {
                // determine if the entity set is unambiguous given the entity type
                if (null != entityDefinition.EntitySets)
                {
                    foreach (EntitySet entitySet in entityDefinition.EntitySets)
                    {
                        if (null != entitySet)
                        {
                            if (entitySet.ElementType.IsAssignableFrom(((CollectionType)treeResultType.EdmType).TypeUsage.EdmType))
                            {
                                if (singleEntitySet == null)
                                {
                                    // found a single match
                                    singleEntitySet = entitySet;
                                }
                                else
                                {
                                    // there's more than one matching entity set
                                    singleEntitySet = null;
                                    break;
                                }
                            }
                        }
                    }
                }
            }

            return(new ObjectQueryExecutionPlan(definition, shaperFactory, treeResultType, mergeOption, singleEntitySet, compiledQueryParameters));
        }
Example #17
0
        internal override ObjectQueryExecutionPlan GetExecutionPlan(
            MergeOption?forMergeOption)
        {
            ObjectQueryExecutionPlan queryExecutionPlan      = (ObjectQueryExecutionPlan)null;
            CompiledQueryCacheEntry  compiledQueryCacheEntry = this._cacheEntry;
            bool comparisonBehavior = this.ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior;

            if (compiledQueryCacheEntry != null)
            {
                MergeOption mergeOption = ObjectQueryState.EnsureMergeOption(forMergeOption, this.UserSpecifiedMergeOption, compiledQueryCacheEntry.PropagatedMergeOption);
                queryExecutionPlan = compiledQueryCacheEntry.GetExecutionPlan(mergeOption, comparisonBehavior);
                if (queryExecutionPlan == null)
                {
                    ExpressionConverter expressionConverter = this.CreateExpressionConverter();
                    DbExpression        query = expressionConverter.Convert();
                    IEnumerable <Tuple <ObjectParameter, QueryParameterExpression> > parameters = expressionConverter.GetParameters();
                    ObjectQueryExecutionPlan newPlan = this._objectQueryExecutionPlanFactory.Prepare(this.ObjectContext, DbQueryCommandTree.FromValidExpression(this.ObjectContext.MetadataWorkspace, DataSpace.CSpace, query, !comparisonBehavior), this.ElementType, mergeOption, this.EffectiveStreamingBehavior, expressionConverter.PropagatedSpan, parameters, expressionConverter.AliasGenerator);
                    queryExecutionPlan = compiledQueryCacheEntry.SetExecutionPlan(newPlan, comparisonBehavior);
                }
            }
            else
            {
                QueryCacheManager     queryCacheManager = this.ObjectContext.MetadataWorkspace.GetQueryCacheManager();
                CompiledQueryCacheKey key = new CompiledQueryCacheKey(this._cacheToken);
                if (queryCacheManager.TryCacheLookup <CompiledQueryCacheKey, CompiledQueryCacheEntry>(key, out compiledQueryCacheEntry))
                {
                    this._cacheEntry = compiledQueryCacheEntry;
                    MergeOption mergeOption = ObjectQueryState.EnsureMergeOption(forMergeOption, this.UserSpecifiedMergeOption, compiledQueryCacheEntry.PropagatedMergeOption);
                    queryExecutionPlan = compiledQueryCacheEntry.GetExecutionPlan(mergeOption, comparisonBehavior);
                }
                if (queryExecutionPlan == null)
                {
                    ExpressionConverter expressionConverter = this.CreateExpressionConverter();
                    DbExpression        query = expressionConverter.Convert();
                    IEnumerable <Tuple <ObjectParameter, QueryParameterExpression> > parameters = expressionConverter.GetParameters();
                    DbQueryCommandTree tree = DbQueryCommandTree.FromValidExpression(this.ObjectContext.MetadataWorkspace, DataSpace.CSpace, query, !comparisonBehavior);
                    if (compiledQueryCacheEntry == null)
                    {
                        compiledQueryCacheEntry = new CompiledQueryCacheEntry((QueryCacheKey)key, expressionConverter.PropagatedMergeOption);
                        QueryCacheEntry outQueryCacheEntry;
                        if (queryCacheManager.TryLookupAndAdd((QueryCacheEntry)compiledQueryCacheEntry, out outQueryCacheEntry))
                        {
                            compiledQueryCacheEntry = (CompiledQueryCacheEntry)outQueryCacheEntry;
                        }
                        this._cacheEntry = compiledQueryCacheEntry;
                    }
                    MergeOption mergeOption = ObjectQueryState.EnsureMergeOption(forMergeOption, this.UserSpecifiedMergeOption, compiledQueryCacheEntry.PropagatedMergeOption);
                    queryExecutionPlan = compiledQueryCacheEntry.GetExecutionPlan(mergeOption, comparisonBehavior);
                    if (queryExecutionPlan == null)
                    {
                        ObjectQueryExecutionPlan newPlan = this._objectQueryExecutionPlanFactory.Prepare(this.ObjectContext, tree, this.ElementType, mergeOption, this.EffectiveStreamingBehavior, expressionConverter.PropagatedSpan, parameters, expressionConverter.AliasGenerator);
                        queryExecutionPlan = compiledQueryCacheEntry.SetExecutionPlan(newPlan, comparisonBehavior);
                    }
                }
            }
            ObjectParameterCollection parameterCollection = this.EnsureParameters();

            if (queryExecutionPlan.CompiledQueryParameters != null && queryExecutionPlan.CompiledQueryParameters.Any <Tuple <ObjectParameter, QueryParameterExpression> >())
            {
                parameterCollection.SetReadOnly(false);
                parameterCollection.Clear();
                foreach (Tuple <ObjectParameter, QueryParameterExpression> compiledQueryParameter in queryExecutionPlan.CompiledQueryParameters)
                {
                    ObjectParameter          objectParameter     = compiledQueryParameter.Item1.ShallowCopy();
                    QueryParameterExpression parameterExpression = compiledQueryParameter.Item2;
                    parameterCollection.Add(objectParameter);
                    if (parameterExpression != null)
                    {
                        objectParameter.Value = parameterExpression.EvaluateParameter(this._parameterValues);
                    }
                }
            }
            parameterCollection.SetReadOnly(true);
            return(queryExecutionPlan);
        }