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