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
- EncoderParameter.cs
- RSAPKCS1KeyExchangeDeformatter.cs
- OleDragDropHandler.cs
- TextSchema.cs
- WebFaultException.cs
- xml.cs
- MetadataCache.cs
- JsonFormatGeneratorStatics.cs
- XmlILOptimizerVisitor.cs
- DeclaredTypeElement.cs
- ConfigViewGenerator.cs
- DataGridViewIntLinkedList.cs
- InputGestureCollection.cs
- SHA384Managed.cs
- XmlSchemaRedefine.cs
- Deserializer.cs
- ServiceNameCollection.cs
- ContentHostHelper.cs
- XmlAnyAttributeAttribute.cs
- NotFiniteNumberException.cs
- PrtTicket_Base.cs
- URIFormatException.cs
- ItemsPresenter.cs
- AxHost.cs
- SelectingProviderEventArgs.cs
- ListMarkerLine.cs
- PolyLineSegment.cs
- WithStatement.cs
- SymbolPair.cs
- ConditionalBranch.cs
- HttpModuleAction.cs
- URLIdentityPermission.cs
- TypeReference.cs
- HttpCapabilitiesSectionHandler.cs
- DataStreamFromComStream.cs
- FileDataSourceCache.cs
- FacetDescriptionElement.cs
- XamlParser.cs
- HandlerBase.cs
- PeerIPHelper.cs
- UrlPath.cs
- OrderablePartitioner.cs
- ComplexObject.cs
- WindowPattern.cs
- IntegerValidatorAttribute.cs
- XmlEnumAttribute.cs
- RecognizeCompletedEventArgs.cs
- GridViewSelectEventArgs.cs
- NTAccount.cs
- GenericTextProperties.cs
- SecurityPolicySection.cs
- cache.cs
- StickyNoteContentControl.cs
- ControlBuilder.cs
- DbConnectionPoolGroupProviderInfo.cs
- XmlUtf8RawTextWriter.cs
- CodeArrayIndexerExpression.cs
- DiscoveryClientReferences.cs
- SymbolEqualComparer.cs
- GeometryCollection.cs
- DSASignatureDeformatter.cs
- InfoCardSymmetricAlgorithm.cs
- ListItemConverter.cs
- SweepDirectionValidation.cs
- CrossSiteScriptingValidation.cs
- MetafileHeader.cs
- LinkAreaEditor.cs
- ColorAnimationUsingKeyFrames.cs
- QilPatternVisitor.cs
- PreservationFileWriter.cs
- ReflectPropertyDescriptor.cs
- Grid.cs
- EndpointNameMessageFilter.cs
- CannotUnloadAppDomainException.cs
- DataGridTableCollection.cs
- HashJoinQueryOperatorEnumerator.cs
- XmlSchemaProviderAttribute.cs
- ValidationEventArgs.cs
- MsmqHostedTransportManager.cs
- Int32Collection.cs
- ByteStream.cs
- ColorKeyFrameCollection.cs
- TypeBuilder.cs
- AppDomainFactory.cs
- DataBindingsDialog.cs
- UpWmlMobileTextWriter.cs
- SelectionService.cs
- GPPOINT.cs
- ModelPropertyCollectionImpl.cs
- ReflectPropertyDescriptor.cs
- TypeConverterAttribute.cs
- UniqueIdentifierService.cs
- UnhandledExceptionEventArgs.cs
- _BufferOffsetSize.cs
- DetailsViewCommandEventArgs.cs
- ToolboxCategoryItems.cs
- MobilePage.cs
- TypefaceMetricsCache.cs
- DataGridViewRowCancelEventArgs.cs
- EditingMode.cs