Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataWeb / Client / System / Data / Services / Client / MemberAssignmentAnalysis.cs / 1305376 / MemberAssignmentAnalysis.cs
//---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // //// Provides a class that can analyze a member assignment to determine // how deep / which entity types the assignment is coming from. // //--------------------------------------------------------------------- namespace System.Data.Services.Client { #region Namespaces. using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Linq.Expressions; using System.Reflection; #endregion Namespaces. ////// Use this class to analyze a member assignment and figure out the /// target path for a member-init on an entity type. /// ////// This class will also detect cases in which the assignment /// expression refers to cases which we shouldn't handle during /// materialization, such as references to multiple entity types /// as sources (or refering to no source at all). /// internal class MemberAssignmentAnalysis : ALinqExpressionVisitor { #region Fields. ///Empty expression array; immutable. internal static readonly Expression[] EmptyExpressionArray = new Expression[0]; ///Entity in scope for the lambda that's providing the parameter. private readonly Expression entity; ///A non-null value when incompatible paths were found for an entity initializer. private Exception incompatibleAssignmentsException; ///Whether multiple paths were found for this analysis. private bool multiplePathsFound; ///Path traversed from the entry field. private ListpathFromEntity; #endregion Fields. #region Constructor. /// Initializes a new /// Entity in scope for the lambda that's providing the parameter. private MemberAssignmentAnalysis(Expression entity) { Debug.Assert(entity != null, "entity != null"); this.entity = entity; this.pathFromEntity = new Listinstance. (); } #endregion Constructor. #region Properties. /// A non-null value when incompatible paths were found for an entity initializer. internal Exception IncompatibleAssignmentsException { get { return this.incompatibleAssignmentsException; } } ///Whether multiple paths were found during analysis. internal bool MultiplePathsFound { get { return this.multiplePathsFound; } } #endregion Properites. #region Methods. ///Analyzes an assignment from a member-init expression. /// Entity in scope for the lambda that's providing the parameter. /// The expression to analyze. ///The analysis results. internal static MemberAssignmentAnalysis Analyze(Expression entityInScope, Expression assignmentExpression) { Debug.Assert(entityInScope != null, "entityInScope != null"); Debug.Assert(assignmentExpression != null, "assignmentExpression != null"); MemberAssignmentAnalysis result = new MemberAssignmentAnalysis(entityInScope); result.Visit(assignmentExpression); return result; } ////// Checks whether the this and a /// Type being initialized. /// Previously seen member accesses (null if this is the first). ////// paths for assignments are compatible. /// An exception to be thrown if assignments are not compatible; null otherwise. ////// This method does not set the IncompatibleAssignmentsException property on either /// analysis instance. /// internal Exception CheckCompatibleAssignments(Type targetType, ref MemberAssignmentAnalysis previous) { if (previous == null) { previous = this; return null; } Expression[] previousExpressions = previous.GetExpressionsToTargetEntity(); Expression[] candidateExpressions = this.GetExpressionsToTargetEntity(); return CheckCompatibleAssignments(targetType, previousExpressions, candidateExpressions); } ///Visits the specified /// Expression to visit. ///. The visited expression. ///This method is overriden to short-circuit analysis once an error is found. internal override Expression Visit(Expression expression) { if (this.multiplePathsFound || this.incompatibleAssignmentsException != null) { return expression; } return base.Visit(expression); } ///Visits a conditional expression. /// Expression to visit. ///The same expression. ////// There are three expressions of interest: the Test, the IfTrue /// branch, and the IfFalse branch. If this is a NullCheck expression, /// then we can traverse the non-null branch, which will be the /// correct path of the resulting value. /// internal override Expression VisitConditional(ConditionalExpression c) { Expression result; var nullCheck = ResourceBinder.PatternRules.MatchNullCheck(this.entity, c); if (nullCheck.Match) { this.Visit(nullCheck.AssignExpression); result = c; } else { result = base.VisitConditional(c); } return result; } ///Parameter visit method. /// Parameter to visit. ///The same expression. internal override Expression VisitParameter(ParameterExpression p) { if (p == this.entity) { if (this.pathFromEntity.Count != 0) { this.multiplePathsFound = true; } else { this.pathFromEntity.Add(p); } } return p; } ///Visits a nested member init. /// Expression to visit. ///The same expression. internal override Expression VisitMemberInit(MemberInitExpression init) { Expression result = init; MemberAssignmentAnalysis previousNested = null; foreach (var binding in init.Bindings) { MemberAssignment assignment = binding as MemberAssignment; if (assignment == null) { continue; } MemberAssignmentAnalysis nested = MemberAssignmentAnalysis.Analyze(this.entity, assignment.Expression); if (nested.MultiplePathsFound) { this.multiplePathsFound = true; break; } // When we're visitng a nested entity initializer, we're exactly one level above that. Exception incompatibleException = nested.CheckCompatibleAssignments(init.Type, ref previousNested); if (incompatibleException != null) { this.incompatibleAssignmentsException = incompatibleException; break; } if (this.pathFromEntity.Count == 0) { this.pathFromEntity.AddRange(nested.GetExpressionsToTargetEntity()); } } return result; } ///Visits a member access expression. /// Access to visit. ///The same expression. internal override Expression VisitMemberAccess(MemberExpression m) { Expression result = base.VisitMemberAccess(m); if (this.pathFromEntity.Contains(m.Expression)) { this.pathFromEntity.Add(m); } return result; } ///Visits a method call expression. /// Method call to visit. ///The same call. internal override Expression VisitMethodCall(MethodCallExpression call) { // When we .Select(), the source of the enumeration is what we contribute // eg: p => p.Cities.Select(c => c) ::= Select(p.Cities, c => c); if (ReflectionUtil.IsSequenceMethod(call.Method, SequenceMethod.Select)) { this.Visit(call.Arguments[0]); return call; } return base.VisitMethodCall(call); } ///Gets the expressions that go beyond the last entity. ///An array of member expressions coming after the last entity. ///Currently a single member access is supported. internal Expression[] GetExpressionsBeyondTargetEntity() { Debug.Assert(!this.multiplePathsFound, "this.multiplePathsFound -- otherwise GetExpressionsToTargetEntity won't return reliable (consistent) results"); if (this.pathFromEntity.Count <= 1) { return EmptyExpressionArray; } Expression[] result = new Expression[1]; result[0] = this.pathFromEntity[this.pathFromEntity.Count - 1]; return result; } ///Gets the expressions that "walk down" to the last entity. ///An array of member expressions down to the last entity. internal Expression[] GetExpressionsToTargetEntity() { Debug.Assert(!this.multiplePathsFound, "this.multiplePathsFound -- otherwise GetExpressionsToTargetEntity won't return reliable (consistent) results"); if (this.pathFromEntity.Count <= 1) { return EmptyExpressionArray; } Expression[] result = new Expression[this.pathFromEntity.Count - 1]; for (int i = 0; i < result.Length; i++) { result[i] = this.pathFromEntity[i]; } return result; } ////// Checks whether the /// Type being initialized. /// Previously seen member accesses. /// Member assignments under evaluate. ///and /// paths for assignments are compatible. /// An exception to be thrown if assignments are not compatible; null otherwise. private static Exception CheckCompatibleAssignments(Type targetType, Expression[] previous, Expression[] candidate) { Debug.Assert(targetType != null, "targetType != null"); Debug.Assert(previous != null, "previous != null"); Debug.Assert(candidate != null, "candidate != null"); if (previous.Length != candidate.Length) { throw CheckCompatibleAssignmentsFail(targetType, previous, candidate); } for (int i = 0; i < previous.Length; i++) { Expression p = previous[i]; Expression c = candidate[i]; if (p.NodeType != c.NodeType) { throw CheckCompatibleAssignmentsFail(targetType, previous, candidate); } if (p == c) { continue; } if (p.NodeType != ExpressionType.MemberAccess) { return CheckCompatibleAssignmentsFail(targetType, previous, candidate); } if (((MemberExpression)p).Member.Name != ((MemberExpression)c).Member.Name) { return CheckCompatibleAssignmentsFail(targetType, previous, candidate); } } return null; } ///Creates an exception to be used when CheckCompatibleAssignment fails. /// Type being initialized. /// Previously seen member accesses. /// Member assignments under evaluate. ///A new exception with diagnostic information. private static Exception CheckCompatibleAssignmentsFail(Type targetType, Expression[] previous, Expression[] candidate) { Debug.Assert(targetType != null, "targetType != null"); Debug.Assert(previous != null, "previous != null"); Debug.Assert(candidate != null, "candidate != null"); string message = Strings.ALinq_ProjectionMemberAssignmentMismatch(targetType.FullName, previous.LastOrDefault(), candidate.LastOrDefault()); return new NotSupportedException(message); } #endregion Methods. } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // //// Provides a class that can analyze a member assignment to determine // how deep / which entity types the assignment is coming from. // //--------------------------------------------------------------------- namespace System.Data.Services.Client { #region Namespaces. using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Linq.Expressions; using System.Reflection; #endregion Namespaces. ////// Use this class to analyze a member assignment and figure out the /// target path for a member-init on an entity type. /// ////// This class will also detect cases in which the assignment /// expression refers to cases which we shouldn't handle during /// materialization, such as references to multiple entity types /// as sources (or refering to no source at all). /// internal class MemberAssignmentAnalysis : ALinqExpressionVisitor { #region Fields. ///Empty expression array; immutable. internal static readonly Expression[] EmptyExpressionArray = new Expression[0]; ///Entity in scope for the lambda that's providing the parameter. private readonly Expression entity; ///A non-null value when incompatible paths were found for an entity initializer. private Exception incompatibleAssignmentsException; ///Whether multiple paths were found for this analysis. private bool multiplePathsFound; ///Path traversed from the entry field. private ListpathFromEntity; #endregion Fields. #region Constructor. /// Initializes a new /// Entity in scope for the lambda that's providing the parameter. private MemberAssignmentAnalysis(Expression entity) { Debug.Assert(entity != null, "entity != null"); this.entity = entity; this.pathFromEntity = new Listinstance. (); } #endregion Constructor. #region Properties. /// A non-null value when incompatible paths were found for an entity initializer. internal Exception IncompatibleAssignmentsException { get { return this.incompatibleAssignmentsException; } } ///Whether multiple paths were found during analysis. internal bool MultiplePathsFound { get { return this.multiplePathsFound; } } #endregion Properites. #region Methods. ///Analyzes an assignment from a member-init expression. /// Entity in scope for the lambda that's providing the parameter. /// The expression to analyze. ///The analysis results. internal static MemberAssignmentAnalysis Analyze(Expression entityInScope, Expression assignmentExpression) { Debug.Assert(entityInScope != null, "entityInScope != null"); Debug.Assert(assignmentExpression != null, "assignmentExpression != null"); MemberAssignmentAnalysis result = new MemberAssignmentAnalysis(entityInScope); result.Visit(assignmentExpression); return result; } ////// Checks whether the this and a /// Type being initialized. /// Previously seen member accesses (null if this is the first). ////// paths for assignments are compatible. /// An exception to be thrown if assignments are not compatible; null otherwise. ////// This method does not set the IncompatibleAssignmentsException property on either /// analysis instance. /// internal Exception CheckCompatibleAssignments(Type targetType, ref MemberAssignmentAnalysis previous) { if (previous == null) { previous = this; return null; } Expression[] previousExpressions = previous.GetExpressionsToTargetEntity(); Expression[] candidateExpressions = this.GetExpressionsToTargetEntity(); return CheckCompatibleAssignments(targetType, previousExpressions, candidateExpressions); } ///Visits the specified /// Expression to visit. ///. The visited expression. ///This method is overriden to short-circuit analysis once an error is found. internal override Expression Visit(Expression expression) { if (this.multiplePathsFound || this.incompatibleAssignmentsException != null) { return expression; } return base.Visit(expression); } ///Visits a conditional expression. /// Expression to visit. ///The same expression. ////// There are three expressions of interest: the Test, the IfTrue /// branch, and the IfFalse branch. If this is a NullCheck expression, /// then we can traverse the non-null branch, which will be the /// correct path of the resulting value. /// internal override Expression VisitConditional(ConditionalExpression c) { Expression result; var nullCheck = ResourceBinder.PatternRules.MatchNullCheck(this.entity, c); if (nullCheck.Match) { this.Visit(nullCheck.AssignExpression); result = c; } else { result = base.VisitConditional(c); } return result; } ///Parameter visit method. /// Parameter to visit. ///The same expression. internal override Expression VisitParameter(ParameterExpression p) { if (p == this.entity) { if (this.pathFromEntity.Count != 0) { this.multiplePathsFound = true; } else { this.pathFromEntity.Add(p); } } return p; } ///Visits a nested member init. /// Expression to visit. ///The same expression. internal override Expression VisitMemberInit(MemberInitExpression init) { Expression result = init; MemberAssignmentAnalysis previousNested = null; foreach (var binding in init.Bindings) { MemberAssignment assignment = binding as MemberAssignment; if (assignment == null) { continue; } MemberAssignmentAnalysis nested = MemberAssignmentAnalysis.Analyze(this.entity, assignment.Expression); if (nested.MultiplePathsFound) { this.multiplePathsFound = true; break; } // When we're visitng a nested entity initializer, we're exactly one level above that. Exception incompatibleException = nested.CheckCompatibleAssignments(init.Type, ref previousNested); if (incompatibleException != null) { this.incompatibleAssignmentsException = incompatibleException; break; } if (this.pathFromEntity.Count == 0) { this.pathFromEntity.AddRange(nested.GetExpressionsToTargetEntity()); } } return result; } ///Visits a member access expression. /// Access to visit. ///The same expression. internal override Expression VisitMemberAccess(MemberExpression m) { Expression result = base.VisitMemberAccess(m); if (this.pathFromEntity.Contains(m.Expression)) { this.pathFromEntity.Add(m); } return result; } ///Visits a method call expression. /// Method call to visit. ///The same call. internal override Expression VisitMethodCall(MethodCallExpression call) { // When we .Select(), the source of the enumeration is what we contribute // eg: p => p.Cities.Select(c => c) ::= Select(p.Cities, c => c); if (ReflectionUtil.IsSequenceMethod(call.Method, SequenceMethod.Select)) { this.Visit(call.Arguments[0]); return call; } return base.VisitMethodCall(call); } ///Gets the expressions that go beyond the last entity. ///An array of member expressions coming after the last entity. ///Currently a single member access is supported. internal Expression[] GetExpressionsBeyondTargetEntity() { Debug.Assert(!this.multiplePathsFound, "this.multiplePathsFound -- otherwise GetExpressionsToTargetEntity won't return reliable (consistent) results"); if (this.pathFromEntity.Count <= 1) { return EmptyExpressionArray; } Expression[] result = new Expression[1]; result[0] = this.pathFromEntity[this.pathFromEntity.Count - 1]; return result; } ///Gets the expressions that "walk down" to the last entity. ///An array of member expressions down to the last entity. internal Expression[] GetExpressionsToTargetEntity() { Debug.Assert(!this.multiplePathsFound, "this.multiplePathsFound -- otherwise GetExpressionsToTargetEntity won't return reliable (consistent) results"); if (this.pathFromEntity.Count <= 1) { return EmptyExpressionArray; } Expression[] result = new Expression[this.pathFromEntity.Count - 1]; for (int i = 0; i < result.Length; i++) { result[i] = this.pathFromEntity[i]; } return result; } ////// Checks whether the /// Type being initialized. /// Previously seen member accesses. /// Member assignments under evaluate. ///and /// paths for assignments are compatible. /// An exception to be thrown if assignments are not compatible; null otherwise. private static Exception CheckCompatibleAssignments(Type targetType, Expression[] previous, Expression[] candidate) { Debug.Assert(targetType != null, "targetType != null"); Debug.Assert(previous != null, "previous != null"); Debug.Assert(candidate != null, "candidate != null"); if (previous.Length != candidate.Length) { throw CheckCompatibleAssignmentsFail(targetType, previous, candidate); } for (int i = 0; i < previous.Length; i++) { Expression p = previous[i]; Expression c = candidate[i]; if (p.NodeType != c.NodeType) { throw CheckCompatibleAssignmentsFail(targetType, previous, candidate); } if (p == c) { continue; } if (p.NodeType != ExpressionType.MemberAccess) { return CheckCompatibleAssignmentsFail(targetType, previous, candidate); } if (((MemberExpression)p).Member.Name != ((MemberExpression)c).Member.Name) { return CheckCompatibleAssignmentsFail(targetType, previous, candidate); } } return null; } ///Creates an exception to be used when CheckCompatibleAssignment fails. /// Type being initialized. /// Previously seen member accesses. /// Member assignments under evaluate. ///A new exception with diagnostic information. private static Exception CheckCompatibleAssignmentsFail(Type targetType, Expression[] previous, Expression[] candidate) { Debug.Assert(targetType != null, "targetType != null"); Debug.Assert(previous != null, "previous != null"); Debug.Assert(candidate != null, "candidate != null"); string message = Strings.ALinq_ProjectionMemberAssignmentMismatch(targetType.FullName, previous.LastOrDefault(), candidate.LastOrDefault()); return new NotSupportedException(message); } #endregion Methods. } } // 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
- MorphHelper.cs
- X509WindowsSecurityToken.cs
- SuppressMergeCheckAttribute.cs
- DataGridItemAttachedStorage.cs
- EmissiveMaterial.cs
- CancelEventArgs.cs
- TextStore.cs
- ObjectReaderCompiler.cs
- ADMembershipProvider.cs
- ParsedRoute.cs
- Policy.cs
- StackOverflowException.cs
- TranslateTransform.cs
- IssuedTokenServiceElement.cs
- DetailsViewUpdatedEventArgs.cs
- FixedSOMLineRanges.cs
- StorageSetMapping.cs
- Merger.cs
- mongolianshape.cs
- AssociatedControlConverter.cs
- ContextMenu.cs
- AspNetHostingPermission.cs
- _ListenerResponseStream.cs
- RequestSecurityTokenResponse.cs
- PipeStream.cs
- PropertyDescriptorCollection.cs
- PictureBox.cs
- StaticExtension.cs
- FlowLayout.cs
- WeakReadOnlyCollection.cs
- RemoteWebConfigurationHostServer.cs
- TypeListConverter.cs
- BlockUIContainer.cs
- _ConnectionGroup.cs
- VariableAction.cs
- VirtualizingStackPanel.cs
- TabItemWrapperAutomationPeer.cs
- PathBox.cs
- AdPostCacheSubstitution.cs
- PerformanceCounterManager.cs
- Polyline.cs
- FreezableDefaultValueFactory.cs
- KeySplineConverter.cs
- ReachFixedPageSerializer.cs
- DesignerLinkAdapter.cs
- ContractUtils.cs
- PointAnimationClockResource.cs
- ExpressionContext.cs
- FixedSOMFixedBlock.cs
- StyleCollectionEditor.cs
- EventHandlerService.cs
- DataGridrowEditEndingEventArgs.cs
- UIElement3DAutomationPeer.cs
- System.Data.OracleClient_BID.cs
- HandleCollector.cs
- UpdatePanelTrigger.cs
- InternalConfigHost.cs
- WebPartConnection.cs
- StylesEditorDialog.cs
- GridViewDeleteEventArgs.cs
- WindowsIdentity.cs
- SqlColumnizer.cs
- SoapTypeAttribute.cs
- GroupBox.cs
- FormViewRow.cs
- Span.cs
- HttpRuntimeSection.cs
- EntityDataSourceWrapper.cs
- DataServiceQueryOfT.cs
- GorillaCodec.cs
- ZipIOExtraField.cs
- MaskedTextBox.cs
- shaper.cs
- NetDataContractSerializer.cs
- DeviceContexts.cs
- SafeCryptContextHandle.cs
- TextCompositionManager.cs
- GlyphShapingProperties.cs
- X509Certificate2.cs
- QueryStringConverter.cs
- initElementDictionary.cs
- ExecutedRoutedEventArgs.cs
- FunctionCommandText.cs
- WebServiceMethodData.cs
- DesignTimeResourceProviderFactoryAttribute.cs
- UnsafeNativeMethods.cs
- RuleAction.cs
- BufferedReadStream.cs
- TemplatedWizardStep.cs
- BindingMemberInfo.cs
- DataSourceXmlTextReader.cs
- ModulesEntry.cs
- HtmlImage.cs
- DataSetFieldSchema.cs
- TextCollapsingProperties.cs
- HwndSubclass.cs
- ResourcePart.cs
- PriorityBindingExpression.cs
- PartBasedPackageProperties.cs
- ParameterReplacerVisitor.cs