MemberAssignmentAnalysis.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / 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 List pathFromEntity;
 
        #endregion Fields. 

        #region Constructor. 

        /// Initializes a new  instance.
        /// 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 List(); 
        }

        #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 
        /// paths for assignments are compatible.
        ///  
        /// Type being initialized.
        /// Previously seen member accesses (null if this is the first). 
        /// 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  and  
        /// paths for assignments are compatible.
        ///  
        /// Type being initialized. 
        /// Previously seen member accesses.
        /// Member assignments under evaluate. 
        /// 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

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK