//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backupOwner [....] //------------------------------------------------------------------------------ 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; /// /// Represents a compiled LINQ ObjectQuery cache entry /// internal sealed class CompiledQueryCacheEntry : QueryCacheEntry { /// /// The merge option that was inferred during expression conversion. /// public readonly MergeOption? PropagatedMergeOption; /// /// A dictionary that contains a plan for each combination of /// merge option and UseCSharpNullComparisonBehavior flag. /// private ConcurrentDictionary _plans; #region Constructors /// /// constructor /// /// The cache key that targets this cache entry /// The inferred merge option that applies to this cached query internal CompiledQueryCacheEntry(QueryCacheKey queryCacheKey, MergeOption? mergeOption) : base(queryCacheKey, null) { this.PropagatedMergeOption = mergeOption; _plans = new ConcurrentDictionary(); } #endregion #region Methods/Properties /// /// 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. /// /// The merge option for which an execution plan is required. /// Flag indicating if C# behavior should be used for null comparisons. /// The corresponding execution plan, if it exists; otherwise null. internal ObjectQueryExecutionPlan GetExecutionPlan(MergeOption mergeOption, bool useCSharpNullComparisonBehavior) { string key = GenerateLocalCacheKey(mergeOption, useCSharpNullComparisonBehavior); ObjectQueryExecutionPlan plan; _plans.TryGetValue(key, out plan); return plan; } /// /// Attempts to set the execution plan for 's merge option and flag on /// this cache entry to . 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 is returned to the caller. /// /// The new execution plan to add to this cache entry. /// Flag indicating if C# behavior should be used for null comparisons. /// The execution plan that corresponds to 's merge option, which may be or may be a previously added execution plan. 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)); } /// /// Convenience method to retrieve the result type from the first non-null execution plan found on this cache entry. /// /// The result type of any execution plan that is or could be added to this cache entry /// true if at least one execution plan was present and a result type could be retrieved; otherwise false 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"); } } } }