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
- DataGridItemEventArgs.cs
- SiteMapNode.cs
- SByteStorage.cs
- SoapAttributeAttribute.cs
- FileAuthorizationModule.cs
- TreeViewImageIndexConverter.cs
- CommentEmitter.cs
- ExpressionEditorAttribute.cs
- SchemaInfo.cs
- MediaPlayerState.cs
- FactoryMaker.cs
- PingOptions.cs
- Condition.cs
- WebPartDeleteVerb.cs
- WebBrowserDesigner.cs
- ProgressBarBrushConverter.cs
- IISMapPath.cs
- GeneralTransformGroup.cs
- TraceHandlerErrorFormatter.cs
- ReplyAdapterChannelListener.cs
- InvalidMessageContractException.cs
- RectangleConverter.cs
- Config.cs
- DoubleLinkList.cs
- HtmlUtf8RawTextWriter.cs
- CodeGenerator.cs
- IdnMapping.cs
- DoubleCollectionConverter.cs
- XsltLoader.cs
- BitmapPalette.cs
- EventRouteFactory.cs
- XamlPointCollectionSerializer.cs
- ExpressionBuilder.cs
- CompositionDesigner.cs
- FtpWebResponse.cs
- parserscommon.cs
- ListViewSortEventArgs.cs
- BindUriHelper.cs
- LayoutManager.cs
- TabPage.cs
- assemblycache.cs
- PrintControllerWithStatusDialog.cs
- ObjectPersistData.cs
- ErasingStroke.cs
- HttpProfileGroupBase.cs
- FileRecordSequenceHelper.cs
- Pipe.cs
- FollowerQueueCreator.cs
- ApplicationSecurityInfo.cs
- CanonicalFormWriter.cs
- versioninfo.cs
- ConstructorBuilder.cs
- Int32KeyFrameCollection.cs
- TrustLevel.cs
- TypeGeneratedEventArgs.cs
- Rule.cs
- LocalClientSecuritySettings.cs
- DSASignatureFormatter.cs
- SecurityTokenProvider.cs
- UserControlDesigner.cs
- TraceHandlerErrorFormatter.cs
- XmlLinkedNode.cs
- ProgressBar.cs
- FullTextState.cs
- PropertyRef.cs
- ExtendedTransformFactory.cs
- RecognizedPhrase.cs
- SmiMetaData.cs
- PageParser.cs
- diagnosticsswitches.cs
- X509Utils.cs
- Query.cs
- EntityClassGenerator.cs
- SqlInfoMessageEvent.cs
- DataControlLinkButton.cs
- WorkflowDebuggerSteppingAttribute.cs
- _SecureChannel.cs
- sqlpipe.cs
- StringExpressionSet.cs
- SuppressMessageAttribute.cs
- ReturnEventArgs.cs
- WebSysDescriptionAttribute.cs
- Sql8ConformanceChecker.cs
- LocatorGroup.cs
- SortAction.cs
- WebServiceTypeData.cs
- ObjectDataSourceMethodEventArgs.cs
- DSGeneratorProblem.cs
- BitmapEffectInput.cs
- TextParentUndoUnit.cs
- Matrix.cs
- QueryCreatedEventArgs.cs
- EnumerableCollectionView.cs
- Int16.cs
- EventMappingSettings.cs
- HttpWebRequestElement.cs
- RemoteDebugger.cs
- SpStreamWrapper.cs
- EventSchemaTraceListener.cs
- LicenseManager.cs