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