Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataEntity / System / Data / Objects / ELinq / Funcletizer.cs / 1305376 / Funcletizer.cs
//---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backupOwner [....] //--------------------------------------------------------------------- using System.Linq; using System.Linq.Expressions; using System.Collections.ObjectModel; using System.Collections.Generic; using System.Data.Metadata.Edm; using System.Data.Common.CommandTrees; using System.Data.Common.CommandTrees.ExpressionBuilder; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Data.Entity; using System.Security; namespace System.Data.Objects.ELinq { ////// Determines which leaves of a LINQ expression tree should be evaluated locally before /// sending a query to the store. These sub-expressions may map to query parameters (e.g. local variables), /// to constants (e.g. literals 'new DateTime(2008, 1, 1)') or query sub-expression /// (e.g. 'context.Products'). Parameter expressions are replaced with QueryParameterExpression /// nodes. All other elements are swapped in place with either expanded expressions (for sub-queries) /// or constants. Where the expression includes mutable state that may influence the translation /// to a query, a Func(Of Boolean) delegate is returned indicating when a recompilation is necessary. /// internal sealed class Funcletizer { // Compiled query information private readonly ParameterExpression _rootContextParameter; private readonly ObjectContext _rootContext; private readonly ConstantExpression _rootContextExpression; private readonly ReadOnlyCollection_compiledQueryParameters; private readonly Mode _mode; private readonly HashSet _linqExpressionStack = new HashSet (); // Object parameters private static readonly string s_parameterPrefix = "p__linq__"; private long _parameterNumber; private Funcletizer( Mode mode, ObjectContext rootContext, ParameterExpression rootContextParameter, ReadOnlyCollection compiledQueryParameters) { _mode = mode; _rootContext = rootContext; _rootContextParameter = rootContextParameter; _compiledQueryParameters = compiledQueryParameters; if (null != _rootContextParameter && null != _rootContext) { _rootContextExpression = Expression.Constant(_rootContext); } } internal static Funcletizer CreateCompiledQueryEvaluationFuncletizer( ObjectContext rootContext, ParameterExpression rootContextParameter, ReadOnlyCollection compiledQueryParameters) { EntityUtil.CheckArgumentNull(rootContext, "rootContext"); EntityUtil.CheckArgumentNull(rootContextParameter, "rootContextParameter"); EntityUtil.CheckArgumentNull(compiledQueryParameters, "compiledQueryParameters"); return new Funcletizer(Mode.CompiledQueryEvaluation, rootContext, rootContextParameter, compiledQueryParameters); } internal static Funcletizer CreateCompiledQueryLockdownFuncletizer() { return new Funcletizer(Mode.CompiledQueryLockdown, null, null, null); } internal static Funcletizer CreateQueryFuncletizer(ObjectContext rootContext) { EntityUtil.CheckArgumentNull(rootContext, "rootContext"); return new Funcletizer(Mode.ConventionalQuery, rootContext, null, null); } internal ObjectContext RootContext { get { return _rootContext; } } internal ParameterExpression RootContextParameter { get { return _rootContextParameter; } } internal ConstantExpression RootContextExpression { get { return _rootContextExpression; } } internal bool IsCompiledQuery { get { return _mode == Mode.CompiledQueryEvaluation || _mode == Mode.CompiledQueryLockdown; } } /// /// Performs funcletization on the given expression. Also returns a delegates that can be used /// to determine if the entire tree needs to be recompiled. /// internal Expression Funcletize(Expression expression, out FuncrecompileRequired) { EntityUtil.CheckArgumentNull(expression, "expression"); // Find all candidates for funcletization. Some sub-expressions are reduced to constants, // others are reduced to variables. The rules vary based on the _mode. Func isClientConstant; Func isClientVariable; expression = ReplaceRootContextParameter(expression); if (_mode == Mode.CompiledQueryEvaluation) { // We lock down closure expressions for compiled queries, so everything is either // a constant or a query parameter produced from the explicit parameters to the // compiled query delegate. isClientConstant = Nominate(expression, this.IsClosureExpression); isClientVariable = Nominate(expression, this.IsCompiledQueryParameterVariable); } else if (_mode == Mode.CompiledQueryLockdown) { // When locking down a compiled query, we can evaluate all closure expressions. isClientConstant = Nominate(expression, this.IsClosureExpression); isClientVariable = (exp) => false; } else { Debug.Assert(_mode == Mode.ConventionalQuery, "No other options..."); // There are no variable parameters outside of compiled queries, so everything is // either a constant or a closure expression. isClientConstant = Nominate(expression, this.IsImmutable); isClientVariable = Nominate(expression, this.IsClosureExpression); } // Now rewrite given nomination functions FuncletizingVisitor visitor = new FuncletizingVisitor(this, isClientConstant, isClientVariable); Expression result = visitor.Visit(expression); recompileRequired = visitor.GetRecompileRequiredFunction(); return result; } /// /// Replaces context parameter (e.g. 'ctx' in CompiledQuery.Compile(ctx => ctx.Products)) with constant /// containing the object context. /// private Expression ReplaceRootContextParameter(Expression expression) { if (null != _rootContextExpression) { return EntityExpressionVisitor.Visit( expression, (exp, baseVisit) => exp == _rootContextParameter ? _rootContextExpression : baseVisit(exp)); } else { return expression; } } ////// Returns a function indicating whether the given expression and all of its children satisfy the /// 'localCriterion'. /// private static FuncNominate(Expression expression, Func localCriterion) { EntityUtil.CheckArgumentNull(localCriterion, "localCriterion"); HashSet candidates = new HashSet (); bool cannotBeNominated = false; Func , Expression> visit = (exp, baseVisit) => { if (exp != null) { bool saveCannotBeNominated = cannotBeNominated; cannotBeNominated = false; baseVisit(exp); if (!cannotBeNominated) { // everyone below me can be nominated, so // see if this one can be also if (localCriterion(exp)) { candidates.Add(exp); } else { cannotBeNominated = true; } } cannotBeNominated |= saveCannotBeNominated; } return exp; }; EntityExpressionVisitor.Visit(expression, visit); return candidates.Contains; } private enum Mode { CompiledQueryLockdown, CompiledQueryEvaluation, ConventionalQuery, } /// /// Determines whether the node may be evaluated locally and whether /// it is a constant. Assumes that all children are also client expressions. /// private bool IsImmutable(Expression expression) { if (null == expression) { return false; } switch (expression.NodeType) { case ExpressionType.New: { // support construction of primitive types PrimitiveType primitiveType; if (!ClrProviderManifest.Instance.TryGetPrimitiveType(TypeSystem.GetNonNullableType(expression.Type), out primitiveType)) { return false; } return true; } case ExpressionType.Constant: return true; case ExpressionType.NewArrayInit: // allow initialization of byte[] 'literals' return (typeof(byte[]) == expression.Type); case ExpressionType.Convert: return true; default: return false; } } ////// Determines whether the node may be evaluated locally and whether /// it is a variable. Assumes that all children are also variable client expressions. /// private bool IsClosureExpression(Expression expression) { if (null == expression) { return false; } if (IsImmutable(expression)) { return true; } if (ExpressionType.MemberAccess == expression.NodeType) { MemberExpression member = (MemberExpression)expression; if (member.Member.MemberType == MemberTypes.Property) { return ExpressionConverter.CanFuncletizePropertyInfo((PropertyInfo)member.Member); } return true; } return false; } ////// Determines whether the node may be evaluated as a compiled query parameter. /// Assumes that all children are also eligible compiled query parameters. /// private bool IsCompiledQueryParameterVariable(Expression expression) { if (null == expression) { return false; } if (IsClosureExpression(expression)) { return true; } if (ExpressionType.Parameter == expression.NodeType) { ParameterExpression parameter = (ParameterExpression)expression; return _compiledQueryParameters.Contains(parameter); } return false; } ////// Determine whether the given CLR type is legal for an ObjectParameter or constant /// DbExpression. /// private bool TryGetTypeUsageForTerminal(Type type, out TypeUsage typeUsage) { EntityUtil.CheckArgumentNull(type, "type"); if (_rootContext.Perspective.TryGetTypeByName(TypeSystem.GetNonNullableType(type).FullName, false, // bIgnoreCase out typeUsage) && (TypeSemantics.IsPrimitiveType(typeUsage) || TypeSemantics.IsEnumerationType(typeUsage))) { return true; } typeUsage = null; return false; } ////// Creates the next available parameter name. /// internal string GenerateParameterName() { // To avoid collisions with user parameters (the full set is not // known at this time) we plug together an 'unlikely' prefix and // a number. return String.Format(CultureInfo.InvariantCulture, "{0}{1}", s_parameterPrefix, _parameterNumber++); } ////// Walks the expression tree and replaces client components with constants /// or QueryParameterExpressions. /// private sealed class FuncletizingVisitor : EntityExpressionVisitor { private readonly Funcletizer _funcletizer; private readonly Func_isClientConstant; private readonly Func _isClientVariable; private readonly List > _recompileRequiredDelegates = new List >(); internal FuncletizingVisitor( Funcletizer funcletizer, Func isClientConstant, Func isClientVariable) { EntityUtil.CheckArgumentNull(funcletizer, "funcletizer"); EntityUtil.CheckArgumentNull(isClientConstant, "isClientConstant"); EntityUtil.CheckArgumentNull(isClientVariable, "isClientVariable"); _funcletizer = funcletizer; _isClientConstant = isClientConstant; _isClientVariable = isClientVariable; } /// /// Returns a delegate indicating (when called) whether a change has been identified /// requiring a complete recompile of the query. /// internal FuncGetRecompileRequiredFunction() { // assign list to local variable to avoid including the entire Funcletizer // class in the closure environment ReadOnlyCollection > recompileRequiredDelegates = _recompileRequiredDelegates.AsReadOnly(); return () => recompileRequiredDelegates.Any(d => d()); } internal override Expression Visit(Expression exp) { if (exp != null) { if (!_funcletizer._linqExpressionStack.Add(exp)) { // This expression is already in the stack. throw EntityUtil.InvalidOperation(Strings.ELinq_CycleDetected); } try { if (_isClientConstant(exp)) { return InlineValue(exp, false); } else if (_isClientVariable(exp)) { TypeUsage queryParameterType; if (_funcletizer.TryGetTypeUsageForTerminal(exp.Type, out queryParameterType)) { DbParameterReferenceExpression parameterReference = queryParameterType.Parameter(_funcletizer.GenerateParameterName()); return new QueryParameterExpression(parameterReference, exp, _funcletizer._compiledQueryParameters); } else if (_funcletizer.IsCompiledQuery) { throw InvalidCompiledQueryParameterException(exp); } else { return InlineValue(exp, true); } } return base.Visit(exp); } finally { _funcletizer._linqExpressionStack.Remove(exp); } } return base.Visit(exp); } private static NotSupportedException InvalidCompiledQueryParameterException(Expression expression) { ParameterExpression parameterExp; if (expression.NodeType == ExpressionType.Parameter) { parameterExp = (ParameterExpression)expression; } else { // 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 = new HashSet (); EntityExpressionVisitor.Visit(expression, (exp, baseVisit) => { if (null != exp && exp.NodeType == ExpressionType.Parameter) { parameters.Add((ParameterExpression)exp); } return baseVisit(exp); }); if (parameters.Count != 1) { return EntityUtil.NotSupported(Strings.CompiledELinq_UnsupportedParameterTypes(expression.Type.FullName)); } parameterExp = parameters.Single(); } if (parameterExp.Type.Equals(expression.Type)) { // If the expression type is the same as the parameter type, indicate that the parameter type is not valid. return EntityUtil.NotSupported(Strings.CompiledELinq_UnsupportedNamedParameterType(parameterExp.Name, parameterExp.Type.FullName)); } else { // Otherwise, indicate that using the specified parameter to produce a value of the expression's type is not supported in compiled query return EntityUtil.NotSupported(Strings.CompiledELinq_UnsupportedNamedParameterUseAsType(parameterExp.Name, expression.Type.FullName)); } } /// /// Compiles a delegate returning the value of the given expression. /// private Func
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- TransportationConfigurationTypeInstallComponent.cs
- OutputCacheProfileCollection.cs
- CookieHandler.cs
- SqlBulkCopyColumnMapping.cs
- TTSEngineTypes.cs
- ContentFileHelper.cs
- ScrollEvent.cs
- ToolStripOverflow.cs
- DefaultValueAttribute.cs
- SoapExtensionImporter.cs
- Main.cs
- ResXFileRef.cs
- SystemTcpStatistics.cs
- ScrollableControl.cs
- OverrideMode.cs
- CriticalExceptions.cs
- WorkflowInstance.cs
- UpdateException.cs
- CryptoApi.cs
- TypeAccessException.cs
- Block.cs
- MaskedTextBoxDesignerActionList.cs
- XmlTextReader.cs
- PackWebRequestFactory.cs
- XmlUtf8RawTextWriter.cs
- WorkflowPrinting.cs
- Canvas.cs
- FacetChecker.cs
- TransformValueSerializer.cs
- CommandConverter.cs
- CDSCollectionETWBCLProvider.cs
- XmlnsCompatibleWithAttribute.cs
- RtType.cs
- OrderedDictionaryStateHelper.cs
- _TransmitFileOverlappedAsyncResult.cs
- DataPointer.cs
- validation.cs
- MarkupCompilePass1.cs
- FrameDimension.cs
- AlignmentXValidation.cs
- AllowedAudienceUriElementCollection.cs
- NavigationProgressEventArgs.cs
- XmlUtil.cs
- DataRowComparer.cs
- ListDataBindEventArgs.cs
- XmlQueryTypeFactory.cs
- _emptywebproxy.cs
- SystemTcpConnection.cs
- MobileControlsSection.cs
- GenericRootAutomationPeer.cs
- BaseContextMenu.cs
- BulletedList.cs
- HttpApplicationFactory.cs
- xmlsaver.cs
- PlainXmlDeserializer.cs
- ErrorRuntimeConfig.cs
- SQLDouble.cs
- EnvironmentPermission.cs
- ProviderBase.cs
- StringFreezingAttribute.cs
- SchemaImporterExtension.cs
- TokenizerHelper.cs
- FrameworkContentElement.cs
- SingletonInstanceContextProvider.cs
- IdentityReference.cs
- SiteMapNodeItem.cs
- BackStopAuthenticationModule.cs
- MD5.cs
- ConfigurationValidatorBase.cs
- _SecureChannel.cs
- AppDomainFactory.cs
- Cloud.cs
- HttpCookieCollection.cs
- TableStyle.cs
- ActivityInterfaces.cs
- MissingFieldException.cs
- TextEffectCollection.cs
- Size.cs
- Pts.cs
- HandlerBase.cs
- AstTree.cs
- SiteIdentityPermission.cs
- DiscreteKeyFrames.cs
- StorageConditionPropertyMapping.cs
- CompilerScope.cs
- DataGridDetailsPresenterAutomationPeer.cs
- EntitySetBase.cs
- DecoderNLS.cs
- WasEndpointConfigContainer.cs
- SqlExpressionNullability.cs
- CommandField.cs
- TextBox.cs
- ThrowHelper.cs
- AdornedElementPlaceholder.cs
- CodeSnippetTypeMember.cs
- HtmlTableRowCollection.cs
- safemediahandle.cs
- TextRangeAdaptor.cs
- ModelPropertyCollectionImpl.cs
- UnicastIPAddressInformationCollection.cs