Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / ndp / fx / src / DataEntity / System / Data / Objects / ELinq / CompiledELinqQueryState.cs / 1 / CompiledELinqQueryState.cs
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// @owner [....]
// @backupOwner [....]
//---------------------------------------------------------------------
namespace System.Data.Objects.ELinq
{
using System;
using System.Collections.Generic;
using System.Data.Common.CommandTrees;
using System.Data.Objects;
using System.Data.Objects.ELinq;
using System.Data.Objects.Internal;
using System.Diagnostics;
using System.Linq;
using CqtExpression = System.Data.Common.CommandTrees.DbExpression;
using LinqExpression = System.Linq.Expressions.Expression;
using System.Data.Metadata.Edm;
using System.Linq.Expressions;
using System.Data.Entity;
using System.Reflection;
using System.Data.Common.QueryCache;
///
/// Models a compiled Linq to Entities ObjectQuery
///
internal sealed class CompiledELinqQueryState : ELinqQueryState
{
private readonly ParameterExpression _rootContextParameter;
private readonly CompiledQueryParameter[] _parameters;
private readonly Guid _cacheToken;
private CompiledQueryCacheEntry _cacheEntry;
///
/// Factory method to create a new compiled query state instance
///
/// The element type of the new instance (the 'T' of the ObjectQuery<T> that the new state instance will back)"
/// The object context with which the new instance should be associated
/// The compiled query definition, as a
/// The parameters that are available to the new compiled query state instance; correspond with the lambda parameters
/// The cache token to use when retrieving or storing the new instance's execution plan in the query cache
/// The new compiled query state instance
internal static CompiledELinqQueryState Create(Type elementType, ObjectContext context, LambdaExpression lambda, Dictionary objectParameters, Guid cacheToken)
{
EntityUtil.CheckArgumentNull(context, "context");
ObjectParameterCollection objectQueryParameters = new ObjectParameterCollection(context.Perspective);
CompiledQueryParameter[] compiledParams = new CompiledQueryParameter[objectParameters.Count];
int i = 0;
foreach (KeyValuePair objectParameter in objectParameters)
{
compiledParams[i] = new CompiledQueryParameter(objectParameter.Key, objectParameter.Value);
try
{
objectQueryParameters.Add(compiledParams[i].ObjectParameter);
}
catch (ArgumentOutOfRangeException)
{
// If this is a simple query parameter (involving a single delegate parameter) report the
// type of that parameter. Otherwise, report the type of the part of the parameter.
HashSet parameters = ParameterExpressionVisitor.FindAllParametersInExpression(objectParameter.Key);
Type parameterType = parameters.Count == 1 ? parameters.Single().Type : objectParameter.Value.ParameterType;
throw EntityUtil.NotSupported(Strings.CompiledELinq_UnsupportedParameterType(parameterType.FullName));
}
i++;
}
return new CompiledELinqQueryState(elementType, context, objectQueryParameters, compiledParams, lambda, cacheToken);
}
private CompiledELinqQueryState(Type elementType, ObjectContext context, ObjectParameterCollection objectParameters, CompiledQueryParameter[] compiledParams, LambdaExpression lambda, Guid cacheToken)
: base(elementType, context, objectParameters, lambda.Body)
{
Debug.Assert(objectParameters != null, "A parameter collection is required");
_rootContextParameter = lambda.Parameters[0];
_parameters = compiledParams;
_cacheToken = cacheToken;
// The parameters available to a compiled query are fixed.
this.EnsureParameters().SetReadOnly(true);
}
internal override ObjectQueryExecutionPlan GetExecutionPlan(MergeOption? forMergeOption)
{
Debug.Assert(this.Span == null, "Include span specified on compiled LINQ-based ObjectQuery instead of within the expression tree?");
Debug.Assert(this._cachedPlan == null, "Cached plan should not be set on compiled LINQ queries");
// Metadata is required to generate the execution plan or to retrieve it from the cache.
this.ObjectContext.EnsureMetadata();
ObjectQueryExecutionPlan plan = null;
CompiledQueryCacheEntry cacheEntry = this._cacheEntry;
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.
MergeOption mergeOption = EnsureMergeOption(forMergeOption, this.UserSpecifiedMergeOption, cacheEntry.PropagatedMergeOption);
// Ask for the corresponding execution plan
plan = cacheEntry.GetExecutionPlan(mergeOption);
if (plan == null)
{
// Convert the LINQ expression to produce a command tree
ExpressionConverter converter = this.CreateExpressionConverter();
DbExpression queryExpression = converter.Convert();
DbQueryCommandTree tree = (DbQueryCommandTree)queryExpression.CommandTree;
tree.Query = queryExpression;
// Prepare the execution plan using the command tree and the computed effective merge option
plan = ObjectQueryExecutionPlan.Prepare(this.ObjectContext, tree, this.ElementType, mergeOption, converter.PropagatedSpan);
// Update and retrieve the execution plan
plan = cacheEntry.SetExecutionPlan(plan);
}
}
else
{
// This instance does not yet have a reference to a cache entry.
// First, attempt to retrieve an existing cache entry.
QueryCacheManager cacheManager = this.ObjectContext.MetadataWorkspace.GetQueryCacheManager();
CompiledQueryCacheKey cacheKey = new CompiledQueryCacheKey(this._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 attempt to retrieve the corresponding execution plan.
this._cacheEntry = cacheEntry;
MergeOption mergeOption = EnsureMergeOption(forMergeOption, this.UserSpecifiedMergeOption, cacheEntry.PropagatedMergeOption);
plan = cacheEntry.GetExecutionPlan(mergeOption);
}
// 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.
ExpressionConverter converter = this.CreateExpressionConverter();
DbExpression queryExpression = converter.Convert();
DbQueryCommandTree tree = (DbQueryCommandTree)queryExpression.CommandTree;
tree.Query = queryExpression;
// 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.
this._cacheEntry = cacheEntry;
}
// Recompute the effective merge option in case a cache entry was just constructed above
MergeOption mergeOption = EnsureMergeOption(forMergeOption, this.UserSpecifiedMergeOption, cacheEntry.PropagatedMergeOption);
// Ask the (retrieved or constructed) cache entry for the corresponding execution plan.
plan = cacheEntry.GetExecutionPlan(mergeOption);
if (plan == null)
{
// The plan is not present, so prepare it now using the computed effective merge option
plan = ObjectQueryExecutionPlan.Prepare(this.ObjectContext, tree, this.ElementType, mergeOption, converter.PropagatedSpan);
// Update the execution plan for the merge option 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);
}
}
}
Debug.Assert(plan != null, "Failed to produce an execution plan?");
return plan;
}
///
/// Overrides GetResultType and attempts to first retrieve the result type from the cache entry.
///
/// The query result type from this compiled query's cache entry, if possible; otherwise defers to
protected override TypeUsage GetResultType()
{
CompiledQueryCacheEntry cacheEntry = this._cacheEntry;
TypeUsage resultType;
if (cacheEntry != null &&
cacheEntry.TryGetResultType(out resultType))
{
return resultType;
}
return base.GetResultType();
}
///
/// Gets a LINQ expression that defines this query.
/// This is overridden to remove parameter references from the underlying expression,
/// producing an expression that contains the values of those parameters as s.
///
internal override Expression Expression
{
get
{
return CreateDonateableExpressionVisitor.Replace(base.Expression, ObjectContext, _rootContextParameter, _parameters);
}
}
///
/// Overrides CreateExpressionConverter to return a converter that uses a binding context based on the compiled query parameters,
/// rather than a default binding context.
///
/// An expression converter appropriate for converting this compiled query state instance
protected override ExpressionConverter CreateExpressionConverter()
{
// Create the command tree used for conversion and add the fixed set of parameters to it
DbQueryCommandTree tree = new DbQueryCommandTree(this.ObjectContext.MetadataWorkspace, DataSpace.CSpace);
foreach (var parameter in _parameters)
{
parameter.CreateParameterReferenceAndAddParameterToCommandTree(tree, this.ObjectContext.Perspective);
}
// Initialize a binding context based on the compiled query parameters.
BindingContext context = new BindingContext(_rootContextParameter, this.ObjectContext, _parameters);
// Return a new expression converter that uses the initialized command tree and binding context.
return new ExpressionConverter(this.ObjectContext, context, tree, base.Expression, this.Parameters);
}
///
/// Replaces ParameterExpresion with ConstantExpression
/// to make the expression usable as a donor expression
///
private sealed class CreateDonateableExpressionVisitor : ExpressionVisitor
{
private readonly Dictionary _parameterToValueLookup;
private readonly BindingContext _bindingContext;
private CreateDonateableExpressionVisitor(BindingContext bindingContext, Dictionary parameterToValueLookup)
{
Debug.Assert(bindingContext != null, "bindingContext must have a value");
Debug.Assert(parameterToValueLookup != null, "parameterToValueLookup must have a value");
_bindingContext = bindingContext;
_parameterToValueLookup = parameterToValueLookup;
}
internal static Expression Replace(Expression input, ObjectContext objectContext, ParameterExpression rootContextParameter, CompiledQueryParameter [] parameters)
{
BindingContext bindingContext = new BindingContext(rootContextParameter, objectContext, parameters);
Dictionary parameterLookup = parameters.ToDictionary(p => p.Expression, p => p.ObjectParameter.Value);
var replacer = new CreateDonateableExpressionVisitor(bindingContext, parameterLookup);
return replacer.Visit(input);
}
///
/// Replace all the known (outer scope) Expressions with ConstantExpressions
///
/// The Expression to replace
/// The new constant expression
internal override Expression Visit(Expression expression)
{
object value;
if (null != expression && _parameterToValueLookup.TryGetValue(expression, out value))
{
return Expression.Constant(value, expression.Type);
}
return base.Visit(expression);
}
internal override Expression VisitMethodCall(MethodCallExpression m)
{
Expression newExpression;
if (TryReplaceRootQuery(m, out newExpression))
{
return newExpression;
}
return base.VisitMethodCall(m);
}
internal override Expression VisitMemberAccess(MemberExpression m)
{
Expression newExpression;
if (TryReplaceRootQuery(m, out newExpression))
{
return newExpression;
}
return base.VisitMemberAccess(m);
}
bool TryReplaceRootQuery(Expression expression, out Expression newExpression)
{
ObjectQuery rootQuery;
if (ExpressionEvaluator.TryEvaluateRootQuery(_bindingContext, expression, out rootQuery))
{
Debug.Assert(typeof(ObjectQuery).IsAssignableFrom(expression.Type), "how did we get a different type");
newExpression = Expression.Constant(rootQuery, expression.Type);
return true;
}
newExpression = null;
return false;
}
}
///
/// Collects parameters expressions from a LINQ expression tree.
///
private sealed class ParameterExpressionVisitor : ExpressionVisitor
{
internal readonly HashSet _parameters = new HashSet();
private ParameterExpressionVisitor()
{
}
internal static HashSet FindAllParametersInExpression(Expression expression)
{
ParameterExpressionVisitor visitor = new ParameterExpressionVisitor();
visitor.Visit(expression);
return visitor._parameters;
}
internal override Expression VisitParameter(ParameterExpression p)
{
_parameters.Add(p);
return base.VisitParameter(p);
}
}
}
internal sealed class CompiledQueryParameter
{
private readonly Expression _expression;
private readonly ObjectParameter _parameter;
private DbParameterReferenceExpression _parameterReference;
internal CompiledQueryParameter(Expression expression, ObjectParameter parameter)
{
_expression = expression;
_parameter = parameter;
}
///
/// Adds the ObjectParameter to the DbCommandTree, and creates a ParameterReferenceExpression
/// to be placed in the proper places in the tree
///
/// The command tree to create operate on.
/// The ClrPerspective to help get the correct TypeUsage.
internal void CreateParameterReferenceAndAddParameterToCommandTree(DbCommandTree commandTree, ClrPerspective perspective)
{
Debug.Assert(_parameterReference == null, "don't call this once it is already setup");
TypeUsage typeUsage;
if(ClosureBinding.TryGetTypeUsageForObjectParameter(_parameter, perspective, out typeUsage))
{
commandTree.AddParameter(_parameter.Name, typeUsage);
_parameterReference = commandTree.CreateParameterReferenceExpression(_parameter.Name);
}
Debug.Assert(_parameterReference != null, "Not sure why this would ever happen, and it isn't handled");
}
internal Expression Expression { get { return _expression; } }
internal ObjectParameter ObjectParameter { get { return _parameter; } }
internal DbParameterReferenceExpression ParameterReference { get { return _parameterReference; } }
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// @owner [....]
// @backupOwner [....]
//---------------------------------------------------------------------
namespace System.Data.Objects.ELinq
{
using System;
using System.Collections.Generic;
using System.Data.Common.CommandTrees;
using System.Data.Objects;
using System.Data.Objects.ELinq;
using System.Data.Objects.Internal;
using System.Diagnostics;
using System.Linq;
using CqtExpression = System.Data.Common.CommandTrees.DbExpression;
using LinqExpression = System.Linq.Expressions.Expression;
using System.Data.Metadata.Edm;
using System.Linq.Expressions;
using System.Data.Entity;
using System.Reflection;
using System.Data.Common.QueryCache;
///
/// Models a compiled Linq to Entities ObjectQuery
///
internal sealed class CompiledELinqQueryState : ELinqQueryState
{
private readonly ParameterExpression _rootContextParameter;
private readonly CompiledQueryParameter[] _parameters;
private readonly Guid _cacheToken;
private CompiledQueryCacheEntry _cacheEntry;
///
/// Factory method to create a new compiled query state instance
///
/// The element type of the new instance (the 'T' of the ObjectQuery<T> that the new state instance will back)"
/// The object context with which the new instance should be associated
/// The compiled query definition, as a
/// The parameters that are available to the new compiled query state instance; correspond with the lambda parameters
/// The cache token to use when retrieving or storing the new instance's execution plan in the query cache
/// The new compiled query state instance
internal static CompiledELinqQueryState Create(Type elementType, ObjectContext context, LambdaExpression lambda, Dictionary objectParameters, Guid cacheToken)
{
EntityUtil.CheckArgumentNull(context, "context");
ObjectParameterCollection objectQueryParameters = new ObjectParameterCollection(context.Perspective);
CompiledQueryParameter[] compiledParams = new CompiledQueryParameter[objectParameters.Count];
int i = 0;
foreach (KeyValuePair objectParameter in objectParameters)
{
compiledParams[i] = new CompiledQueryParameter(objectParameter.Key, objectParameter.Value);
try
{
objectQueryParameters.Add(compiledParams[i].ObjectParameter);
}
catch (ArgumentOutOfRangeException)
{
// If this is a simple query parameter (involving a single delegate parameter) report the
// type of that parameter. Otherwise, report the type of the part of the parameter.
HashSet parameters = ParameterExpressionVisitor.FindAllParametersInExpression(objectParameter.Key);
Type parameterType = parameters.Count == 1 ? parameters.Single().Type : objectParameter.Value.ParameterType;
throw EntityUtil.NotSupported(Strings.CompiledELinq_UnsupportedParameterType(parameterType.FullName));
}
i++;
}
return new CompiledELinqQueryState(elementType, context, objectQueryParameters, compiledParams, lambda, cacheToken);
}
private CompiledELinqQueryState(Type elementType, ObjectContext context, ObjectParameterCollection objectParameters, CompiledQueryParameter[] compiledParams, LambdaExpression lambda, Guid cacheToken)
: base(elementType, context, objectParameters, lambda.Body)
{
Debug.Assert(objectParameters != null, "A parameter collection is required");
_rootContextParameter = lambda.Parameters[0];
_parameters = compiledParams;
_cacheToken = cacheToken;
// The parameters available to a compiled query are fixed.
this.EnsureParameters().SetReadOnly(true);
}
internal override ObjectQueryExecutionPlan GetExecutionPlan(MergeOption? forMergeOption)
{
Debug.Assert(this.Span == null, "Include span specified on compiled LINQ-based ObjectQuery instead of within the expression tree?");
Debug.Assert(this._cachedPlan == null, "Cached plan should not be set on compiled LINQ queries");
// Metadata is required to generate the execution plan or to retrieve it from the cache.
this.ObjectContext.EnsureMetadata();
ObjectQueryExecutionPlan plan = null;
CompiledQueryCacheEntry cacheEntry = this._cacheEntry;
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.
MergeOption mergeOption = EnsureMergeOption(forMergeOption, this.UserSpecifiedMergeOption, cacheEntry.PropagatedMergeOption);
// Ask for the corresponding execution plan
plan = cacheEntry.GetExecutionPlan(mergeOption);
if (plan == null)
{
// Convert the LINQ expression to produce a command tree
ExpressionConverter converter = this.CreateExpressionConverter();
DbExpression queryExpression = converter.Convert();
DbQueryCommandTree tree = (DbQueryCommandTree)queryExpression.CommandTree;
tree.Query = queryExpression;
// Prepare the execution plan using the command tree and the computed effective merge option
plan = ObjectQueryExecutionPlan.Prepare(this.ObjectContext, tree, this.ElementType, mergeOption, converter.PropagatedSpan);
// Update and retrieve the execution plan
plan = cacheEntry.SetExecutionPlan(plan);
}
}
else
{
// This instance does not yet have a reference to a cache entry.
// First, attempt to retrieve an existing cache entry.
QueryCacheManager cacheManager = this.ObjectContext.MetadataWorkspace.GetQueryCacheManager();
CompiledQueryCacheKey cacheKey = new CompiledQueryCacheKey(this._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 attempt to retrieve the corresponding execution plan.
this._cacheEntry = cacheEntry;
MergeOption mergeOption = EnsureMergeOption(forMergeOption, this.UserSpecifiedMergeOption, cacheEntry.PropagatedMergeOption);
plan = cacheEntry.GetExecutionPlan(mergeOption);
}
// 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.
ExpressionConverter converter = this.CreateExpressionConverter();
DbExpression queryExpression = converter.Convert();
DbQueryCommandTree tree = (DbQueryCommandTree)queryExpression.CommandTree;
tree.Query = queryExpression;
// 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.
this._cacheEntry = cacheEntry;
}
// Recompute the effective merge option in case a cache entry was just constructed above
MergeOption mergeOption = EnsureMergeOption(forMergeOption, this.UserSpecifiedMergeOption, cacheEntry.PropagatedMergeOption);
// Ask the (retrieved or constructed) cache entry for the corresponding execution plan.
plan = cacheEntry.GetExecutionPlan(mergeOption);
if (plan == null)
{
// The plan is not present, so prepare it now using the computed effective merge option
plan = ObjectQueryExecutionPlan.Prepare(this.ObjectContext, tree, this.ElementType, mergeOption, converter.PropagatedSpan);
// Update the execution plan for the merge option 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);
}
}
}
Debug.Assert(plan != null, "Failed to produce an execution plan?");
return plan;
}
///
/// Overrides GetResultType and attempts to first retrieve the result type from the cache entry.
///
/// The query result type from this compiled query's cache entry, if possible; otherwise defers to
protected override TypeUsage GetResultType()
{
CompiledQueryCacheEntry cacheEntry = this._cacheEntry;
TypeUsage resultType;
if (cacheEntry != null &&
cacheEntry.TryGetResultType(out resultType))
{
return resultType;
}
return base.GetResultType();
}
///
/// Gets a LINQ expression that defines this query.
/// This is overridden to remove parameter references from the underlying expression,
/// producing an expression that contains the values of those parameters as s.
///
internal override Expression Expression
{
get
{
return CreateDonateableExpressionVisitor.Replace(base.Expression, ObjectContext, _rootContextParameter, _parameters);
}
}
///
/// Overrides CreateExpressionConverter to return a converter that uses a binding context based on the compiled query parameters,
/// rather than a default binding context.
///
/// An expression converter appropriate for converting this compiled query state instance
protected override ExpressionConverter CreateExpressionConverter()
{
// Create the command tree used for conversion and add the fixed set of parameters to it
DbQueryCommandTree tree = new DbQueryCommandTree(this.ObjectContext.MetadataWorkspace, DataSpace.CSpace);
foreach (var parameter in _parameters)
{
parameter.CreateParameterReferenceAndAddParameterToCommandTree(tree, this.ObjectContext.Perspective);
}
// Initialize a binding context based on the compiled query parameters.
BindingContext context = new BindingContext(_rootContextParameter, this.ObjectContext, _parameters);
// Return a new expression converter that uses the initialized command tree and binding context.
return new ExpressionConverter(this.ObjectContext, context, tree, base.Expression, this.Parameters);
}
///
/// Replaces ParameterExpresion with ConstantExpression
/// to make the expression usable as a donor expression
///
private sealed class CreateDonateableExpressionVisitor : ExpressionVisitor
{
private readonly Dictionary _parameterToValueLookup;
private readonly BindingContext _bindingContext;
private CreateDonateableExpressionVisitor(BindingContext bindingContext, Dictionary parameterToValueLookup)
{
Debug.Assert(bindingContext != null, "bindingContext must have a value");
Debug.Assert(parameterToValueLookup != null, "parameterToValueLookup must have a value");
_bindingContext = bindingContext;
_parameterToValueLookup = parameterToValueLookup;
}
internal static Expression Replace(Expression input, ObjectContext objectContext, ParameterExpression rootContextParameter, CompiledQueryParameter [] parameters)
{
BindingContext bindingContext = new BindingContext(rootContextParameter, objectContext, parameters);
Dictionary parameterLookup = parameters.ToDictionary(p => p.Expression, p => p.ObjectParameter.Value);
var replacer = new CreateDonateableExpressionVisitor(bindingContext, parameterLookup);
return replacer.Visit(input);
}
///
/// Replace all the known (outer scope) Expressions with ConstantExpressions
///
/// The Expression to replace
/// The new constant expression
internal override Expression Visit(Expression expression)
{
object value;
if (null != expression && _parameterToValueLookup.TryGetValue(expression, out value))
{
return Expression.Constant(value, expression.Type);
}
return base.Visit(expression);
}
internal override Expression VisitMethodCall(MethodCallExpression m)
{
Expression newExpression;
if (TryReplaceRootQuery(m, out newExpression))
{
return newExpression;
}
return base.VisitMethodCall(m);
}
internal override Expression VisitMemberAccess(MemberExpression m)
{
Expression newExpression;
if (TryReplaceRootQuery(m, out newExpression))
{
return newExpression;
}
return base.VisitMemberAccess(m);
}
bool TryReplaceRootQuery(Expression expression, out Expression newExpression)
{
ObjectQuery rootQuery;
if (ExpressionEvaluator.TryEvaluateRootQuery(_bindingContext, expression, out rootQuery))
{
Debug.Assert(typeof(ObjectQuery).IsAssignableFrom(expression.Type), "how did we get a different type");
newExpression = Expression.Constant(rootQuery, expression.Type);
return true;
}
newExpression = null;
return false;
}
}
///
/// Collects parameters expressions from a LINQ expression tree.
///
private sealed class ParameterExpressionVisitor : ExpressionVisitor
{
internal readonly HashSet _parameters = new HashSet();
private ParameterExpressionVisitor()
{
}
internal static HashSet FindAllParametersInExpression(Expression expression)
{
ParameterExpressionVisitor visitor = new ParameterExpressionVisitor();
visitor.Visit(expression);
return visitor._parameters;
}
internal override Expression VisitParameter(ParameterExpression p)
{
_parameters.Add(p);
return base.VisitParameter(p);
}
}
}
internal sealed class CompiledQueryParameter
{
private readonly Expression _expression;
private readonly ObjectParameter _parameter;
private DbParameterReferenceExpression _parameterReference;
internal CompiledQueryParameter(Expression expression, ObjectParameter parameter)
{
_expression = expression;
_parameter = parameter;
}
///
/// Adds the ObjectParameter to the DbCommandTree, and creates a ParameterReferenceExpression
/// to be placed in the proper places in the tree
///
/// The command tree to create operate on.
/// The ClrPerspective to help get the correct TypeUsage.
internal void CreateParameterReferenceAndAddParameterToCommandTree(DbCommandTree commandTree, ClrPerspective perspective)
{
Debug.Assert(_parameterReference == null, "don't call this once it is already setup");
TypeUsage typeUsage;
if(ClosureBinding.TryGetTypeUsageForObjectParameter(_parameter, perspective, out typeUsage))
{
commandTree.AddParameter(_parameter.Name, typeUsage);
_parameterReference = commandTree.CreateParameterReferenceExpression(_parameter.Name);
}
Debug.Assert(_parameterReference != null, "Not sure why this would ever happen, and it isn't handled");
}
internal Expression Expression { get { return _expression; } }
internal ObjectParameter ObjectParameter { get { return _parameter; } }
internal DbParameterReferenceExpression ParameterReference { get { return _parameterReference; } }
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- RotateTransform.cs
- XmlImplementation.cs
- RC2.cs
- BevelBitmapEffect.cs
- TemplateColumn.cs
- VectorAnimationBase.cs
- HttpCookieCollection.cs
- XPathNodeHelper.cs
- ChildrenQuery.cs
- EventEntry.cs
- RenderOptions.cs
- ExpressionPrinter.cs
- CaseInsensitiveComparer.cs
- CodeThrowExceptionStatement.cs
- Point4DConverter.cs
- SingleConverter.cs
- MatrixCamera.cs
- PackageRelationship.cs
- SecurityTokenValidationException.cs
- EditableRegion.cs
- ImageList.cs
- SystemIcons.cs
- ReadOnlyDataSourceView.cs
- FullTextLine.cs
- Page.cs
- MultiView.cs
- ScriptMethodAttribute.cs
- Enlistment.cs
- RayMeshGeometry3DHitTestResult.cs
- Compiler.cs
- dtdvalidator.cs
- RemoteWebConfigurationHost.cs
- DataPagerFieldItem.cs
- SecurityTokenTypes.cs
- SecureStringHasher.cs
- BitmapData.cs
- ListChunk.cs
- WinInetCache.cs
- AlgoModule.cs
- OleAutBinder.cs
- AuthorizationSection.cs
- AuthenticatingEventArgs.cs
- Processor.cs
- ObjectNotFoundException.cs
- EntityDataSourceSelectingEventArgs.cs
- EnumValAlphaComparer.cs
- DbConvert.cs
- TemplateGroupCollection.cs
- GenericEnumerator.cs
- Utils.cs
- TdsParser.cs
- ViewEvent.cs
- ToolStripMenuItemCodeDomSerializer.cs
- OpCellTreeNode.cs
- SizeValueSerializer.cs
- ObjectListCommandsPage.cs
- ReflectionHelper.cs
- CodeIndexerExpression.cs
- ToolBarButtonClickEvent.cs
- WebErrorHandler.cs
- WasEndpointConfigContainer.cs
- DbDataAdapter.cs
- XmlChildEnumerator.cs
- StreamGeometryContext.cs
- DataGridViewImageCell.cs
- DefaultConfirmation.cs
- XmlSiteMapProvider.cs
- FormViewDeletedEventArgs.cs
- EUCJPEncoding.cs
- FragmentQuery.cs
- DiscardableAttribute.cs
- Renderer.cs
- InkCanvasFeedbackAdorner.cs
- SoapCodeExporter.cs
- HostedNamedPipeTransportManager.cs
- ProfileProvider.cs
- _HelperAsyncResults.cs
- GraphicsContainer.cs
- SecurityException.cs
- TypedReference.cs
- PointHitTestResult.cs
- HitTestWithGeometryDrawingContextWalker.cs
- GestureRecognizer.cs
- IdentitySection.cs
- RegexGroupCollection.cs
- RawStylusInputCustomData.cs
- PolyLineSegment.cs
- TextTreeInsertElementUndoUnit.cs
- PropagatorResult.cs
- ClientRuntime.cs
- ActivityExecutorDelegateInfo.cs
- TextDecorationLocationValidation.cs
- ContainerUIElement3D.cs
- TaskFileService.cs
- SplineKeyFrames.cs
- XD.cs
- CodeMethodInvokeExpression.cs
- OracleParameter.cs
- ServiceOperationInvoker.cs
- DeferredReference.cs