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
- TokenBasedSetEnumerator.cs
- RootDesignerSerializerAttribute.cs
- Rect3D.cs
- TranslateTransform.cs
- XmlLanguageConverter.cs
- StreamWithDictionary.cs
- EnumValAlphaComparer.cs
- TreeNodeCollection.cs
- DecimalKeyFrameCollection.cs
- UnionCqlBlock.cs
- SqlProcedureAttribute.cs
- ConstraintCollection.cs
- MarkupWriter.cs
- BooleanExpr.cs
- ProfileGroupSettings.cs
- TypeInfo.cs
- StrokeRenderer.cs
- TokenCreationException.cs
- Point3D.cs
- WebPartConnection.cs
- TableCellAutomationPeer.cs
- RoleManagerSection.cs
- StateBag.cs
- GiveFeedbackEvent.cs
- FileDataSource.cs
- TitleStyle.cs
- CompositeScriptReference.cs
- SemaphoreFullException.cs
- WebResponse.cs
- CodeSubDirectory.cs
- CreateUserWizardStep.cs
- Lasso.cs
- BrowserTree.cs
- DecimalConverter.cs
- ManipulationDeltaEventArgs.cs
- ProcessInfo.cs
- PictureBox.cs
- PasswordPropertyTextAttribute.cs
- Group.cs
- XmlArrayItemAttribute.cs
- ValidateNames.cs
- _NtlmClient.cs
- GPPOINT.cs
- SQLInt32Storage.cs
- AssertFilter.cs
- WindowsFormsHost.cs
- NotifyIcon.cs
- ColorIndependentAnimationStorage.cs
- WebConfigurationManager.cs
- ColumnBinding.cs
- HttpCacheVaryByContentEncodings.cs
- TextEndOfParagraph.cs
- DataTableNameHandler.cs
- HttpCapabilitiesSectionHandler.cs
- DefaultClaimSet.cs
- PassportAuthenticationEventArgs.cs
- SimpleHandlerFactory.cs
- NativeMethods.cs
- TouchDevice.cs
- IPHostEntry.cs
- KeyToListMap.cs
- ResumeStoryboard.cs
- ProfessionalColorTable.cs
- SystemIPInterfaceProperties.cs
- Connection.cs
- OutputCacheSettingsSection.cs
- dtdvalidator.cs
- StylusTip.cs
- SingleResultAttribute.cs
- ResolveCompletedEventArgs.cs
- HelpProvider.cs
- WeakReference.cs
- NegatedConstant.cs
- ExpressionList.cs
- PartBasedPackageProperties.cs
- MLangCodePageEncoding.cs
- SimpleLine.cs
- GuidelineSet.cs
- CalendarBlackoutDatesCollection.cs
- RefreshPropertiesAttribute.cs
- XmlSchemas.cs
- OverflowException.cs
- HelpProvider.cs
- Menu.cs
- MethodExpr.cs
- CngProperty.cs
- WsiProfilesElementCollection.cs
- SapiRecoInterop.cs
- UserPreferenceChangedEventArgs.cs
- MaterialCollection.cs
- PackageDigitalSignatureManager.cs
- IntegrationExceptionEventArgs.cs
- ExecutorLocksHeldException.cs
- EncodingConverter.cs
- XmlAtomErrorReader.cs
- RegexMatchCollection.cs
- CodeDirectiveCollection.cs
- Page.cs
- basenumberconverter.cs
- EventWaitHandle.cs