File: System\Data\Common\QueryCache\CompiledQueryCacheEntry.cs
Project: ndp\fx\src\DataEntity\System.Data.Entity.csproj (System.Data.Entity)
//------------------------------------------------------------------------------
// <copyright file="CompiledQueryCacheEntry.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner  Microsoft
// @backupOwner Microsoft
//------------------------------------------------------------------------------
 
namespace System.Data.Common.QueryCache
{
    using System;
    using System.Data.Metadata.Edm;
    using System.Data.Objects;
    using System.Data.Objects.Internal;
    using System.Diagnostics;
    using System.Threading;
    using System.Collections.Concurrent;
 
    /// <summary> 
    /// Represents a compiled LINQ ObjectQuery cache entry
    /// </summary> 
    internal sealed class CompiledQueryCacheEntry : QueryCacheEntry
    {
        /// <summary>
        /// The merge option that was inferred during expression conversion.
        /// </summary>
        public readonly MergeOption? PropagatedMergeOption;
 
        /// <summary>
        /// A dictionary that contains a plan for each combination of
        /// merge option and UseCSharpNullComparisonBehavior flag.
        /// </summary>
        private ConcurrentDictionary<String, ObjectQueryExecutionPlan> _plans;
 
        #region Constructors
        /// <summary> 
        /// constructor 
        /// </summary> 
        /// <param name="queryCacheKey">The cache key that targets this cache entry</param>
        /// <param name="mergeOption">The inferred merge option that applies to this cached query</param>
        internal CompiledQueryCacheEntry(QueryCacheKey queryCacheKey, MergeOption? mergeOption)
            : base(queryCacheKey, null)
        {
            this.PropagatedMergeOption = mergeOption;
            _plans = new ConcurrentDictionary<string,ObjectQueryExecutionPlan>();
        }
        #endregion
 
        #region Methods/Properties
 
        /// <summary>
        /// Retrieves the execution plan for the specified merge option and UseCSharpNullComparisonBehavior flag. May return null if the 
        /// plan for the given merge option and useCSharpNullComparisonBehavior flag is not present.
        /// </summary>
        /// <param name="mergeOption">The merge option for which an execution plan is required.</param>
        /// <param name="useCSharpNullComparisonBehavior">Flag indicating if C# behavior should be used for null comparisons.</param>
        /// <returns>The corresponding execution plan, if it exists; otherwise <c>null</c>.</returns>
        internal ObjectQueryExecutionPlan GetExecutionPlan(MergeOption mergeOption, bool useCSharpNullComparisonBehavior)
        {
            string key = GenerateLocalCacheKey(mergeOption, useCSharpNullComparisonBehavior);
            ObjectQueryExecutionPlan plan;
            _plans.TryGetValue(key, out plan);
            return plan;
        }
 
        /// <summary>
        /// Attempts to set the execution plan for <paramref name="newPlan"/>'s merge option and <paramref name="useCSharpNullComparisonBehavior"/> flag on 
        /// this cache entry to <paramref name="newPlan"/>. If a plan already exists for that merge option and UseCSharpNullComparisonBehavior flag, the 
        /// current value is not changed but is returned to the caller. Otherwise <paramref name="newPlan"/> is returned to the caller.
        /// </summary>
        /// <param name="newPlan">The new execution plan to add to this cache entry.</param>
        /// <param name="useCSharpNullComparisonBehavior">Flag indicating if C# behavior should be used for null comparisons.</param>
        /// <returns>The execution plan that corresponds to <paramref name="newPlan"/>'s merge option, which may be <paramref name="newPlan"/> or may be a previously added execution plan.</returns>
        internal ObjectQueryExecutionPlan SetExecutionPlan(ObjectQueryExecutionPlan newPlan, bool useCSharpNullComparisonBehavior)
        {
            Debug.Assert(newPlan != null, "New plan cannot be null");
 
            string planKey = GenerateLocalCacheKey(newPlan.MergeOption, useCSharpNullComparisonBehavior);
            // Get the value if it is there. If not, add it and get it.
            return (_plans.GetOrAdd(planKey, newPlan));
        }
 
        /// <summary>
        /// Convenience method to retrieve the result type from the first non-null execution plan found on this cache entry.
        /// </summary>
        /// <param name="resultType">The result type of any execution plan that is or could be added to this cache entry</param>
        /// <returns><c>true</c> if at least one execution plan was present and a result type could be retrieved; otherwise <c>false</c></returns>
        internal bool TryGetResultType(out TypeUsage resultType)
        {
            foreach (var value in _plans.Values)
            {
                resultType = value.ResultType;
                return true;
            }
            resultType = null;
            return false;
        }
 
        #endregion
 
        internal override object GetTarget()
        {
            return this;
        }
 
        private string GenerateLocalCacheKey(MergeOption mergeOption, bool useCSharpNullComparisonBehavior)
        {
            switch (mergeOption)
            {
                case MergeOption.AppendOnly:
                case MergeOption.NoTracking:
                case MergeOption.OverwriteChanges:
                case MergeOption.PreserveChanges:
                    return string.Join("", Enum.GetName(typeof(MergeOption), mergeOption), useCSharpNullComparisonBehavior);
                default:
                    throw EntityUtil.ArgumentOutOfRange("newPlan.MergeOption");
            }
        }
    }
}