MemberPath.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / ndp / fx / src / DataEntity / System / Data / Map / ViewGeneration / Structures / MemberPath.cs / 3 / MemberPath.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 

using System; 
using System.Data.Common.Utils;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics; 
using System.Data.Mapping.ViewGeneration.CqlGeneration;
using System.Data.Metadata.Edm; 
using System.Linq; 

namespace System.Data.Mapping.ViewGeneration.Structures 
{

    // A class that corresponds to a path in some extent, e.g., Person,
    // Person.addr, Person.addr.state 
    internal class MemberPath : InternalBase, IEquatable
    { 
 
        #region Constructors
        // effects: Creates a memberpath that corresponds to path in some 
        // extent (or an extent itself)
        internal MemberPath(EntitySetBase extent, IEnumerable path, MetadataWorkspace workspace)
        {
            Debug.Assert(workspace != null, "Workspace must never be null"); 
            m_extent = extent;
            m_path = new List(path); 
            m_workspace = workspace; 
        }
 
        // effects: Creates a memberpath that corresponds to path in some
        // extent (or an extent itself)
        internal MemberPath(EntitySetBase extent, MetadataWorkspace workspace)
            : this(extent, new EdmMember[] { }, workspace) 
        {
        } 
 
        // effects: Creates the path corresponding to "extent.member"
        internal MemberPath(EntitySetBase extent, EdmMember member, MetadataWorkspace workspace) 
            : this(extent, new EdmMember[] { member }, workspace)
        {
        }
 
        // effects: Given a path "prefix" and a member "last",
        // creates a memberpath corresponding to the path "prefix.last" 
        internal MemberPath(MemberPath prefix, EdmMember last) 
        {
            m_extent = prefix.m_extent; 
            m_path = new List(prefix.m_path);
            m_path.Add(last);
            m_workspace = prefix.MetadataWorkspace;
        } 
        #endregion
 
        #region Fields 
        // The base entity set and list of members in the path
        private EntitySetBase m_extent; 
        private List m_path;
        private MetadataWorkspace m_workspace;

        internal static readonly IEqualityComparer EqualityComparer = new MemberPathComparer(); 
        #endregion
 
        #region Properties 
        internal string LastComponentName
        { 
            get
            {
                if (m_path.Count == 0)
                { 
                    return m_extent.Name;
                } 
                else 
                {
                    return LastMember.Name; 
                }
            }
        }
 
        /// 
        /// Tells whether the first member is a computed member, as 
        ///  
        internal bool IsComputed
        { 
            get
            {
                if (m_path.Count == 0)
                { 
                    return false;
                } 
                else 
                {
                    return FirstMember.IsStoreGeneratedComputed; 
                }
            }
        }
 
        // effects: Returns the default value stored in the metadata for
        // this. If no default value is present, returns null 
        internal object DefaultValue 
        {
            get 
            {
                if (m_path.Count == 0)
                {
                    return null; 
                }
                Facet facet; 
                if (LastMember.TypeUsage.Facets.TryGetValue(EdmProviderManifest.DefaultValueFacetName, false, out facet)) 
                {
                    return facet.Value; 
                }
                return null;
            }
        } 

        // effects: Returns true if the last element of this is part of the key 
        internal bool IsPartOfKey 
        {
            get 
            {
                if (m_path.Count == 0)
                {
                    return false; 
                }
                EdmProperty property = LastMember as EdmProperty; 
                Debug.Assert(property != null || LastMember is AssociationEndMember); 
                return MetadataHelper.IsPartOfEntityTypeKey(property);
            } 
        }

        internal bool IsNullable
        { 
            get
            { 
                if (m_path.Count == 0) 
                {
                    return false; 
                }
                return MetadataHelper.IsMemberNullable(LastMember);
            }
        } 

        // requires: this corresponds to an entity set or an association set end 
        internal EntitySet EntitySet 
        {
            get 
            {
                Debug.Assert(m_path.Count <= 1);
                if (m_path.Count == 0)
                { 
                    return (EntitySet)m_extent; // Require clause says it is an entity set
                } 
                else 
                {
                    AssociationEndMember endMember = (AssociationEndMember)FirstMember; 
                    EntitySet result = MetadataHelper.GetEntitySetAtEnd((AssociationSet)m_extent, endMember);
                    return result;
                }
            } 
        }
 
        internal EntitySetBase Extent 
        {
            get { return m_extent; } 
        }

        // effects: Returns the type of attribute denoted by the path
        // For example, member type of Person.addr.zip would be integer. For 
        // extent, it is the element type
        internal EdmType EdmType 
        { 
            get
            { 
                if (m_path.Count > 0)
                {
                    return LastMember.TypeUsage.EdmType;
                } 
                else
                { 
                    return m_extent.ElementType; 
                }
            } 
        }

        internal IList Members
        { 
            get { return m_path; }
        } 
 
        // requires: this has at least one element below the extent
        // effects: Returns the last property in this path 
        internal EdmMember LastMember
        {
            get
            { 
                Debug.Assert(m_path.Count > 0);
                return m_path[m_path.Count - 1]; 
            } 
        }
 

        // requires: this has at least one element below the extent
        // effects: Returns the first property in this path
        internal EdmMember FirstMember 
        {
            get 
            { 
                Debug.Assert(m_path.Count > 0);
                return m_path[0]; 
            }
        }

        // effects: Given a memberpath and block alias, returns a valid Cql 
        // identifier of the form "blockAlias.identfier". If tableAlias is
        // null, just returns identifier 
        internal string CqlFieldAlias 
        {
            get 
            {
                string alias = PathToString(true);
                if (false == alias.Contains("_"))
                { 
                    // if alias the member does not contain any _, we can replace
                    // the . with _ so that we can get a simple identifier 
                    alias = alias.Replace('.', '_'); 
                }
 
                StringBuilder builder = new StringBuilder();
                CqlWriter.AppendEscapedName(builder, alias);
                return builder.ToString();
            } 
        }
 
        // effects: returns false iff this is 
        // * A descendant of some nullable property
        // * A descendant of an optional composition/collection 
        // * A descendant of a property that does not belong to the
        //   basetype/rootype of its parent
        internal bool IsAlwaysDefined(Dictionary> inheritanceGraph)
        { 
            if (m_path.Count == 0)
            { 
                // Extents are always defined 
                return true;
            } 

            EdmMember member = m_path.Last();

            //Dont check last member, thats the property we are testing 
            for (int i = 0; i< m_path.Count - 1; i++)
            { 
                EdmMember current = m_path[i]; 
                // If member is nullable then "this" will not always be defined
                if (MetadataHelper.IsMemberNullable(current)) 
                {
                    return false;
                }
            } 

            //Now check if there are any concrete types other than all subtypes of Type defining this member 
 
            //by definition association types member are always present since they are IDs
            if (m_path[0].DeclaringType is AssociationType) 
            {
                return true;
            }
 
            EntityType entitySetType = m_extent.ElementType as EntityType;
            if (entitySetType == null) //association type 
            { 
                return true;
            } 

            //well, we handle the first case because we don't knwo how to get to subtype (i.e. the edge to avoid)
            EntityType memberDeclaringType = m_path[0].DeclaringType as EntityType;
            EntityType parentType = memberDeclaringType.BaseType as EntityType; 

 
            if (entitySetType.EdmEquals(memberDeclaringType) || MetadataHelper.IsParentOf(memberDeclaringType, entitySetType) || parentType == null) 
            {
                return true; 
            }
            else if (!parentType.Abstract && !MetadataHelper.DoesMemberExist(parentType, member))
            {
                return false; 
            }
 
            bool result = !RecurseToFindMemberAbsentInConcreteType(parentType, memberDeclaringType, member, entitySetType, inheritanceGraph); 
            return result;
        } 

        private static bool RecurseToFindMemberAbsentInConcreteType(EntityType current, EntityType avoidEdge, EdmMember member, EntityType entitySetType, Dictionary> inheritanceGraph)
        {
            Set edges = inheritanceGraph[current]; 

            //for each outgoing edge (from current) where the edge is not the one to avoid, 
            // navigate depth-first 
            foreach (var edge in edges.Where(type => !type.EdmEquals(avoidEdge)))
            { 
                //Dont traverse above the EntitySet's Element type
                if (entitySetType.BaseType!=null && entitySetType.BaseType.EdmEquals(edge))
                {
                    continue; 
                }
 
                if(!edge.Abstract && !MetadataHelper.DoesMemberExist(edge, member)) 
                {
                    //found it.. I'm the concrete type that has member absent. 
                    return true;
                }

                if (RecurseToFindMemberAbsentInConcreteType(edge, current /*avoid traversing down back here*/, member, entitySetType, inheritanceGraph)) 
                {
                    //one of the edges reachable from me found it 
                    return true; 
                }
            } 
            //no body found this counter example
            return false;
        }
 
        internal MetadataWorkspace MetadataWorkspace { get { return m_workspace; } }
        #endregion 
 
        #region Methods
        // effects: Determines all the identifiers used in this and adds them to identifiers 
        internal void GetIdentifiers(CqlIdentifiers identifiers)
        {
            // Get the extent name and extent type name
            identifiers.AddIdentifier(m_extent.Name); 
            identifiers.AddIdentifier(m_extent.ElementType.Name);
            foreach (EdmMember member in m_path) 
            { 
                identifiers.AddIdentifier(member.Name);
            } 
        }

        // effects: Returns true iff all members are nullable properties, i.e., if even one
        // of them is non-nullable, returns false 
        internal static bool AreAllMembersNullable(IEnumerable members)
        { 
            foreach (MemberPath path in members) 
            {
                if (path.m_path.Count == 0) 
                {
                    return false; // Extents are not nullable
                }
                if (path.IsNullable == false) 
                {
                    return false; 
                } 
            }
            return true; 
        }

        // effects: Returns a string that has the list of properties in
        // members (i.e., just the last name) if fullPath is false. Else the 
        // fullPAth is added
        internal static string PropertiesToUserString(IEnumerable members, bool fullPath) 
        { 
            bool isFirst = true;
            StringBuilder builder = new StringBuilder(); 
            foreach (MemberPath path in members)
            {
                if (isFirst == false)
                { 
                    builder.Append(", ");
                } 
                isFirst = false; 
                if (fullPath)
                { 
                    builder.Append(path.PathToString(false));
                }
                else
                { 
                    builder.Append(path.LastComponentName);
                } 
            } 
            return builder.ToString();
        } 

        // effects: Given a member path and an alias, returns a string
        // correspondng to the fully-qualified name tableAlias.path
        // e.g.,  T1.Address.Phone.Zip. If a subcomponent belongs to 
        // subclass, generates a treat for it, e.g. TREAT(T1 as Customer).Address
        // Or even TREAT(TREAT(T1 AS Customer).Address as USAddress).Zip 
        // Returns the modified builder 
        internal StringBuilder AsCql(StringBuilder inputBuilder, string blockAlias)
        { 
            // Due to the TREAT stuff, we cannot build incrementally. So we use a local
            // StringBuilder -- it should not be that inefficient (one extra copy)

            StringBuilder builder = new StringBuilder(); 
            CqlWriter.AppendEscapedName(builder, blockAlias);
            // Keep track of the previous type so that we can determine if we 
            // need to cast or not 
            EdmType prevType = m_extent.ElementType;
 
            foreach (EdmMember member in m_path)
            {

                // If prevType is a ref (e.g., ref to CPerson), we need to get the 
                // type that it is pointing to and then look for this member in that
 
                StructuralType prevStructuralType; 
                RefType refType = null;
                if (Helper.IsRefType(prevType)) 
                {
                    refType = (RefType)prevType;
                    prevStructuralType = refType.ElementType;
                } 
                else
                { 
                    prevStructuralType = (StructuralType)prevType; 
                }
 
                // Check whether the prevType has the present member in it.
                // If not, we will need to cast the prev type to the
                // appropriate subtype
                bool found = MetadataHelper.DoesMemberExist(prevStructuralType, member); 

                // For reference types, the key must be present in the element type itself. 
                // E.g., if we have Ref(CPerson), the key must be present as CPerson.pid 
                // or CPerson.Address.Phone.Number (i.e., in a complex type). Note that it cannot
                // be present in the subtype of address or phone either, i.e., this path 
                // better not have any TREAT calls
                // We are at CPerson right now. So if we say Key(CPerson), we will get a row with
                // all the key elements. Then we can continue going down the path in CPerson
                if (refType != null) 
                {
                    Debug.Assert(found == true, "We did not find the key property in " + 
                                 "a ref's element type - it cannot be in a subtype"); 
                    // Emit an extract key from the ref
                    builder.Insert(0, "Key("); 
                    builder.Append(")");
                }
                else if (false == found)
                { 
                    // Need to add Treat(... as ...) expression in the
                    // beginning. Note that it does handle cases like 
                    // TREAT(TREAT(T1 AS Customer).Address as USAddress).Zip 
                    Debug.Assert(!Helper.IsRefType(prevType), "We do not allow subtyping in key extraction from Refs");
                    builder.Insert(0, "TREAT("); 
                    builder.Append(" AS ");
                    CqlWriter.AppendEscapedTypeName(builder, member.DeclaringType);
                    builder.Append(')');
                } 

                // Add the member's name. We had a path "T1.A.B" till now 
                builder.Append('.'); 
                CqlWriter.AppendEscapedName(builder, member.Name);
                prevType = member.TypeUsage.EdmType; 
            }
            inputBuilder.Append(builder.ToString());
            return inputBuilder;
        } 

        public bool Equals(MemberPath right) 
        { 
            return EqualityComparer.Equals(this, right);
        } 

        public override bool Equals(object obj)
        {
            MemberPath right = obj as MemberPath; 
            if (obj == null)
            { 
                return false; 
            }
            return Equals(right); 
        }

        public override int GetHashCode()
        { 
            return EqualityComparer.GetHashCode(this);
        } 
 
        // effects: Returns true if the element denoted by the path corresponds to a scalar (primitive or enum)
        internal bool IsScalarType() 
        {
            return EdmType.BuiltInTypeKind == BuiltInTypeKind.PrimitiveType ||
                   EdmType.BuiltInTypeKind == BuiltInTypeKind.EnumType;
        } 

        internal static IEnumerable GetKeyMembers(EntitySetBase extent, MemberDomainMap domainMap, MetadataWorkspace workspace) 
        { 
            MemberPath extentPath = new MemberPath(extent, workspace);
            List keyAttributes = new List( 
                extentPath.GetMembers(extentPath.Extent.ElementType, null /* isScalar */,  null /* isConditional */, true /* isPartOfKey */, domainMap));
            Debug.Assert(keyAttributes.Any(), "No key attributes?");
            return keyAttributes;
        } 

        internal IEnumerable GetMembers(EdmType edmType, bool? isScalar, bool? isConditional, bool? isPartOfKey, MemberDomainMap domainMap) 
        { 
            MemberPath currentPath = this;
            StructuralType structuralType = (StructuralType)edmType; 
            foreach (EdmMember edmMember in structuralType.Members)
            {
                if (edmMember is AssociationEndMember)
                { 
                    // get end's keys
                    foreach (MemberPath endKey in new MemberPath(currentPath, edmMember).GetMembers( 
                                                         ((RefType)edmMember.TypeUsage.EdmType).ElementType, 
                                                         isScalar, isConditional, true /*isPartOfKey*/, domainMap))
                    { 
                        yield return endKey;
                    }
                }
                bool isActuallyScalar = MetadataHelper.IsNonRefSimpleMember(edmMember); 
                if (isScalar == null || isScalar == isActuallyScalar)
                { 
                    EdmProperty childProperty = edmMember as EdmProperty; 
                    if (childProperty != null)
                    { 
                        bool isActuallyKey = MetadataHelper.IsPartOfEntityTypeKey(childProperty);
                        if (isPartOfKey == null || isPartOfKey == isActuallyKey)
                        {
                            MemberPath childPath = new MemberPath(currentPath, childProperty); 
                            bool isActuallyConditional = domainMap.IsConditionMember(childPath);
                            if (isConditional == null || isConditional == isActuallyConditional) 
                            { 
                                yield return childPath;
                            } 
                        }
                    }
                }
            } 
        }
 
        // effects: Returns true if this and path1 are equivalent on the C-side via a referential constraint 
        internal bool IsEquivalentViaRefConstraint(MemberPath path1)
        { 
            MemberPath path0 = this;

            // Now check if they are equivalent via referential constraint
 
            // For example,
            // * Person.pid and PersonAddress.Person.pid are equivalent 
            // * Person.pid and PersonAddress.Address.pid are equivalent 
            // * Person.pid and Address.pid are equivalent if there is a referential constraint
            // * PersonAddress.Person.pid and PersonAddress.Address.pid are 
            //   equivalent if there is a referential constraint

            // In short, Person.pid, Address.pid, PersonAddress.Address.pid,
            // PersonAddress.Person.pid are the same 

            if (path0.EdmType is EntityTypeBase || path1.EdmType is EntityTypeBase || 
                MetadataHelper.IsNonRefSimpleMember(path0.LastMember) == false || 
                MetadataHelper.IsNonRefSimpleMember(path1.LastMember) == false)
            { 
                // If the path corresponds to a top level extent only, ignore
                // it. Or if it is not a scalar
                return false;
            } 

            AssociationSet assocSet0 = path0.Extent as AssociationSet; 
            AssociationSet assocSet1 = path1.Extent as AssociationSet; 
            EntitySet entitySet0 = path0.Extent as EntitySet;
            EntitySet entitySet1 = path1.Extent as EntitySet; 
            bool result = false;

            if (assocSet0 != null && assocSet1 != null)
            { 
                // PersonAddress.Person.pid and PersonAddress.Address.pid case
                // Check if they are the same association or not 
                if (assocSet0.Equals(assocSet1) == false) 
                {
                    return false; 
                }
                result = AreAssocationEndPathsEquivalentViaRefConstraint(path0, path1, assocSet0);
            }
            else if (entitySet0 != null && entitySet1 != null) 
            {
                // Person.pid, Address.pid case 
                // Find all the associations between the two sets. If the 
                // fields are equivalent via any association + referential
                // constraint, return true 
                List assocSets = MetadataHelper.GetAssociationsForEntitySets(entitySet0, entitySet1);
                foreach (AssociationSet assocSet in assocSets)
                {
                    // For Person.pid, get PersonAddress.Person.pid or 
                    MemberPath assocEndPath0 = path0.GetCorrespondingAssociationPath(assocSet);
                    MemberPath assocEndPath1 = path1.GetCorrespondingAssociationPath(assocSet); 
                    if (AreAssocationEndPathsEquivalentViaRefConstraint(assocEndPath0, assocEndPath1, assocSet)) 
                    {
                        result = true; 
                        break;
                    }
                }
 
            }
            else 
            { 
                // One of them is an assocSet and the other is an entity set
                AssociationSet assocSet = assocSet0 != null ? assocSet0 : assocSet1; 
                EntitySet entitySet = entitySet0 != null ? entitySet0 : entitySet1;
                Debug.Assert(assocSet != null && entitySet != null,
                             "One set must be association and the other must be entity set");
 
                MemberPath assocEndPathA = path0.Extent is AssociationSet ? path0 : path1;
                MemberPath entityPath = path0.Extent is EntitySet ? path0 : path1; 
                MemberPath assocEndPathB = entityPath.GetCorrespondingAssociationPath(assocSet); 
                if (assocEndPathB == null)
                { 
                    //An EntitySet might participate in multiple AssociationSets
                    //and this might not be the association set that defines the expected referential
                    //constraint
                    //Return false since this does not have any referential constraint specified 
                    result = false;
                } 
                else 
                {
                    result = AreAssocationEndPathsEquivalentViaRefConstraint(assocEndPathA, assocEndPathB, assocSet); 
                }
            }

            return result; 
        }
 
        // requires: path0 and path1 correspond to paths in assocSet 
        // effects: Returns true if assocPath0 and assocPath1 are
        // equivalent via a referential constraint in assocSet 
        private static bool AreAssocationEndPathsEquivalentViaRefConstraint(MemberPath assocPath0,
                                                                            MemberPath assocPath1,
                                                                            AssociationSet assocSet)
        { 
            Debug.Assert(assocPath0.Extent.Equals(assocSet) && assocPath1.Extent.Equals(assocSet),
                         "Extent for paths must be assocSet"); 
 
            AssociationEndMember end0 = assocPath0.FirstMember as AssociationEndMember;
            AssociationEndMember end1 = assocPath1.FirstMember as AssociationEndMember; 
            EdmProperty property0 = assocPath0.LastMember as EdmProperty;
            EdmProperty property1 = assocPath1.LastMember as EdmProperty;

            if (end0 == null || end1 == null || property0 == null || property1 == null) 
            {
                return false; 
            } 

            // Now check if these fields are connected via a referential constraint 
            AssociationType assocType = assocSet.ElementType;
            bool foundConstraint = false;

            foreach (ReferentialConstraint constraint in assocType.ReferentialConstraints) 
            {
                bool isFrom0 = end0.Name == constraint.FromRole.Name && 
                    end1.Name == constraint.ToRole.Name; 
                bool isFrom1 = end1.Name == constraint.FromRole.Name &&
                    end0.Name == constraint.ToRole.Name; 

                if (isFrom0 || isFrom1)
                {
                    // Found an RI for the two sets. Make sure that the properties are at the same ordinal 

                    // isFrom0 is true when end0 corresponds to FromRole and end1 to ToRole 
                    ReadOnlyMetadataCollection properties0 = isFrom0 ? constraint.FromProperties : constraint.ToProperties; 
                    ReadOnlyMetadataCollection properties1 = isFrom0 ? constraint.ToProperties : constraint.FromProperties;
                    int indexForPath0 = properties0.IndexOf(property0); 
                    int indexForPath1 = properties1.IndexOf(property1);
                    if (indexForPath0 == indexForPath1 && indexForPath0 != -1)
                    {
                        foundConstraint = true; 
                        break;
                    } 
                } 
            }
            return foundConstraint; 
        }

        // Note: this need not correspond to a key field of an entity set E and
        // assocSet corresponds to an association whose one end is E 
        //
        // effects: Returns the MemberPath corresponding to that field in the 
        // assocSet. E.g., given Address.pid, returns PersonAddress.Address.pid. 
        // For self-associations, such as ManagerEmployee with referential
        // constraints (and we have [, 
        // ]), given Employee.mid, returns
        // ManagerEmployee.Employee.mid or ManagerEmployee.Manager.mid
        private MemberPath GetCorrespondingAssociationPath(AssociationSet assocSet)
        { 
            Debug.Assert(Extent is EntitySet, "path must correspond to entity");
 
            // Find the end corresponding to the entity set 
            AssociationEndMember end = MetadataHelper.GetSomeEndForEntitySet(assocSet, (EntitySet)m_extent);
            //An EntitySet might participate in multiple AssociationSets 
            //and this might not be the association set that defines the expected referential
            //constraint
            if (end == null)
            { 
                return null;
            } 
            // Create the new members using the end 
            List newMembers = new List();
            newMembers.Add(end); 
            newMembers.AddRange(m_path);
            // The extent is the assocSet
            MemberPath result = new MemberPath(assocSet, newMembers, m_workspace);
            return result; 
        }
 
        // effects: If this identifies a relationship end, return its scope. 
        // Otherwise, returns null.
        internal EntitySet GetScopeOfRelationEnd() 
        {
            if (m_path.Count == 0)
            {
                return null; 
            }
 
            AssociationEndMember relationEndMember = LastMember as AssociationEndMember; 
            if (relationEndMember == null)
            { 
                return null;
            }

            // Yes, it's a reference, determine its entity set refScope 
            AssociationSet associationSet = (AssociationSet)m_extent;
            EntitySet result = MetadataHelper.GetEntitySetAtEnd(associationSet, relationEndMember); 
            return result; 
        }
 
        // effects: Returns a string of the form a.b.c that corresponds to
        // the "this's" path -- This string can be used for tests or
        // localization
        // If forAlias is true, we return a string that is relevant for Cql 
        // aliases, else we return the exact path
        internal string PathToString(bool? forAlias) 
        { 
            StringBuilder builder = new StringBuilder();
 
            if (forAlias != null)
            {
                if (forAlias==true)
                { 
                    // For the 0th entry, we just choose the type of the element in
                    // which the first entry belongs, e.g., if Addr belongs to CCustomer, 
                    // we choose CCustomer and not CPerson. 
                    if (m_path.Count == 0)
                    { 
                        EntityTypeBase type = m_extent.ElementType;
                        return type.Name;
                    }
                    builder.Append(m_path[0].DeclaringType.Name); // Get CCustomer here 
                }
                else 
                { 
                    // Append the extent name
                    builder.Append(m_extent.Name); 
                }
            }

            // Just join the path using "." 
            for (int i = 0; i < m_path.Count; i++)
            { 
                builder.Append('.'); 
                builder.Append(m_path[i].Name);
            } 
            return builder.ToString();
        }

        // effects: Returns a human-readable string corresponding to this 
        internal override void ToCompactString(StringBuilder builder)
        { 
            builder.Append(PathToString(false)); 
        }
 
        internal void ToCompactString(StringBuilder builder, string instanceToken)
        {
            builder.Append(instanceToken+PathToString(null));
        } 

        #endregion 
 
        #region Comparer
        private class MemberPathComparer : IEqualityComparer 
        {

            public bool Equals(MemberPath left, MemberPath right)
            { 
                if (object.ReferenceEquals(left, right))
                { 
                    return true; 
                }
                // One of them is non-null at least. So if the other one is 
                // null, we cannot be equal
                if (left == null || right == null)
                {
                    return false; 
                }
                // Both are non-null at this point 
                // Checks that the paths are equal component-wise 
                if (left.m_extent.Equals(right.m_extent) == false || left.m_path.Count != right.m_path.Count)
                { 
                    return false;
                }

                for (int i = 0; i < left.m_path.Count; i++) 
                {
                    // Comparing MemberMetadata -- can use Equals 
                    if (false == left.m_path[i].Equals(right.m_path[i])) 
                    {
                        return false; 
                    }
                }
                return true;
            } 

            public int GetHashCode(MemberPath key) 
            { 
                int result = key.m_extent.GetHashCode();
                foreach (EdmMember member in key.m_path) 
                {
                    result ^= member.GetHashCode();
                }
                return result; 
            }
        } 
        #endregion 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 

using System; 
using System.Data.Common.Utils;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics; 
using System.Data.Mapping.ViewGeneration.CqlGeneration;
using System.Data.Metadata.Edm; 
using System.Linq; 

namespace System.Data.Mapping.ViewGeneration.Structures 
{

    // A class that corresponds to a path in some extent, e.g., Person,
    // Person.addr, Person.addr.state 
    internal class MemberPath : InternalBase, IEquatable
    { 
 
        #region Constructors
        // effects: Creates a memberpath that corresponds to path in some 
        // extent (or an extent itself)
        internal MemberPath(EntitySetBase extent, IEnumerable path, MetadataWorkspace workspace)
        {
            Debug.Assert(workspace != null, "Workspace must never be null"); 
            m_extent = extent;
            m_path = new List(path); 
            m_workspace = workspace; 
        }
 
        // effects: Creates a memberpath that corresponds to path in some
        // extent (or an extent itself)
        internal MemberPath(EntitySetBase extent, MetadataWorkspace workspace)
            : this(extent, new EdmMember[] { }, workspace) 
        {
        } 
 
        // effects: Creates the path corresponding to "extent.member"
        internal MemberPath(EntitySetBase extent, EdmMember member, MetadataWorkspace workspace) 
            : this(extent, new EdmMember[] { member }, workspace)
        {
        }
 
        // effects: Given a path "prefix" and a member "last",
        // creates a memberpath corresponding to the path "prefix.last" 
        internal MemberPath(MemberPath prefix, EdmMember last) 
        {
            m_extent = prefix.m_extent; 
            m_path = new List(prefix.m_path);
            m_path.Add(last);
            m_workspace = prefix.MetadataWorkspace;
        } 
        #endregion
 
        #region Fields 
        // The base entity set and list of members in the path
        private EntitySetBase m_extent; 
        private List m_path;
        private MetadataWorkspace m_workspace;

        internal static readonly IEqualityComparer EqualityComparer = new MemberPathComparer(); 
        #endregion
 
        #region Properties 
        internal string LastComponentName
        { 
            get
            {
                if (m_path.Count == 0)
                { 
                    return m_extent.Name;
                } 
                else 
                {
                    return LastMember.Name; 
                }
            }
        }
 
        /// 
        /// Tells whether the first member is a computed member, as 
        ///  
        internal bool IsComputed
        { 
            get
            {
                if (m_path.Count == 0)
                { 
                    return false;
                } 
                else 
                {
                    return FirstMember.IsStoreGeneratedComputed; 
                }
            }
        }
 
        // effects: Returns the default value stored in the metadata for
        // this. If no default value is present, returns null 
        internal object DefaultValue 
        {
            get 
            {
                if (m_path.Count == 0)
                {
                    return null; 
                }
                Facet facet; 
                if (LastMember.TypeUsage.Facets.TryGetValue(EdmProviderManifest.DefaultValueFacetName, false, out facet)) 
                {
                    return facet.Value; 
                }
                return null;
            }
        } 

        // effects: Returns true if the last element of this is part of the key 
        internal bool IsPartOfKey 
        {
            get 
            {
                if (m_path.Count == 0)
                {
                    return false; 
                }
                EdmProperty property = LastMember as EdmProperty; 
                Debug.Assert(property != null || LastMember is AssociationEndMember); 
                return MetadataHelper.IsPartOfEntityTypeKey(property);
            } 
        }

        internal bool IsNullable
        { 
            get
            { 
                if (m_path.Count == 0) 
                {
                    return false; 
                }
                return MetadataHelper.IsMemberNullable(LastMember);
            }
        } 

        // requires: this corresponds to an entity set or an association set end 
        internal EntitySet EntitySet 
        {
            get 
            {
                Debug.Assert(m_path.Count <= 1);
                if (m_path.Count == 0)
                { 
                    return (EntitySet)m_extent; // Require clause says it is an entity set
                } 
                else 
                {
                    AssociationEndMember endMember = (AssociationEndMember)FirstMember; 
                    EntitySet result = MetadataHelper.GetEntitySetAtEnd((AssociationSet)m_extent, endMember);
                    return result;
                }
            } 
        }
 
        internal EntitySetBase Extent 
        {
            get { return m_extent; } 
        }

        // effects: Returns the type of attribute denoted by the path
        // For example, member type of Person.addr.zip would be integer. For 
        // extent, it is the element type
        internal EdmType EdmType 
        { 
            get
            { 
                if (m_path.Count > 0)
                {
                    return LastMember.TypeUsage.EdmType;
                } 
                else
                { 
                    return m_extent.ElementType; 
                }
            } 
        }

        internal IList Members
        { 
            get { return m_path; }
        } 
 
        // requires: this has at least one element below the extent
        // effects: Returns the last property in this path 
        internal EdmMember LastMember
        {
            get
            { 
                Debug.Assert(m_path.Count > 0);
                return m_path[m_path.Count - 1]; 
            } 
        }
 

        // requires: this has at least one element below the extent
        // effects: Returns the first property in this path
        internal EdmMember FirstMember 
        {
            get 
            { 
                Debug.Assert(m_path.Count > 0);
                return m_path[0]; 
            }
        }

        // effects: Given a memberpath and block alias, returns a valid Cql 
        // identifier of the form "blockAlias.identfier". If tableAlias is
        // null, just returns identifier 
        internal string CqlFieldAlias 
        {
            get 
            {
                string alias = PathToString(true);
                if (false == alias.Contains("_"))
                { 
                    // if alias the member does not contain any _, we can replace
                    // the . with _ so that we can get a simple identifier 
                    alias = alias.Replace('.', '_'); 
                }
 
                StringBuilder builder = new StringBuilder();
                CqlWriter.AppendEscapedName(builder, alias);
                return builder.ToString();
            } 
        }
 
        // effects: returns false iff this is 
        // * A descendant of some nullable property
        // * A descendant of an optional composition/collection 
        // * A descendant of a property that does not belong to the
        //   basetype/rootype of its parent
        internal bool IsAlwaysDefined(Dictionary> inheritanceGraph)
        { 
            if (m_path.Count == 0)
            { 
                // Extents are always defined 
                return true;
            } 

            EdmMember member = m_path.Last();

            //Dont check last member, thats the property we are testing 
            for (int i = 0; i< m_path.Count - 1; i++)
            { 
                EdmMember current = m_path[i]; 
                // If member is nullable then "this" will not always be defined
                if (MetadataHelper.IsMemberNullable(current)) 
                {
                    return false;
                }
            } 

            //Now check if there are any concrete types other than all subtypes of Type defining this member 
 
            //by definition association types member are always present since they are IDs
            if (m_path[0].DeclaringType is AssociationType) 
            {
                return true;
            }
 
            EntityType entitySetType = m_extent.ElementType as EntityType;
            if (entitySetType == null) //association type 
            { 
                return true;
            } 

            //well, we handle the first case because we don't knwo how to get to subtype (i.e. the edge to avoid)
            EntityType memberDeclaringType = m_path[0].DeclaringType as EntityType;
            EntityType parentType = memberDeclaringType.BaseType as EntityType; 

 
            if (entitySetType.EdmEquals(memberDeclaringType) || MetadataHelper.IsParentOf(memberDeclaringType, entitySetType) || parentType == null) 
            {
                return true; 
            }
            else if (!parentType.Abstract && !MetadataHelper.DoesMemberExist(parentType, member))
            {
                return false; 
            }
 
            bool result = !RecurseToFindMemberAbsentInConcreteType(parentType, memberDeclaringType, member, entitySetType, inheritanceGraph); 
            return result;
        } 

        private static bool RecurseToFindMemberAbsentInConcreteType(EntityType current, EntityType avoidEdge, EdmMember member, EntityType entitySetType, Dictionary> inheritanceGraph)
        {
            Set edges = inheritanceGraph[current]; 

            //for each outgoing edge (from current) where the edge is not the one to avoid, 
            // navigate depth-first 
            foreach (var edge in edges.Where(type => !type.EdmEquals(avoidEdge)))
            { 
                //Dont traverse above the EntitySet's Element type
                if (entitySetType.BaseType!=null && entitySetType.BaseType.EdmEquals(edge))
                {
                    continue; 
                }
 
                if(!edge.Abstract && !MetadataHelper.DoesMemberExist(edge, member)) 
                {
                    //found it.. I'm the concrete type that has member absent. 
                    return true;
                }

                if (RecurseToFindMemberAbsentInConcreteType(edge, current /*avoid traversing down back here*/, member, entitySetType, inheritanceGraph)) 
                {
                    //one of the edges reachable from me found it 
                    return true; 
                }
            } 
            //no body found this counter example
            return false;
        }
 
        internal MetadataWorkspace MetadataWorkspace { get { return m_workspace; } }
        #endregion 
 
        #region Methods
        // effects: Determines all the identifiers used in this and adds them to identifiers 
        internal void GetIdentifiers(CqlIdentifiers identifiers)
        {
            // Get the extent name and extent type name
            identifiers.AddIdentifier(m_extent.Name); 
            identifiers.AddIdentifier(m_extent.ElementType.Name);
            foreach (EdmMember member in m_path) 
            { 
                identifiers.AddIdentifier(member.Name);
            } 
        }

        // effects: Returns true iff all members are nullable properties, i.e., if even one
        // of them is non-nullable, returns false 
        internal static bool AreAllMembersNullable(IEnumerable members)
        { 
            foreach (MemberPath path in members) 
            {
                if (path.m_path.Count == 0) 
                {
                    return false; // Extents are not nullable
                }
                if (path.IsNullable == false) 
                {
                    return false; 
                } 
            }
            return true; 
        }

        // effects: Returns a string that has the list of properties in
        // members (i.e., just the last name) if fullPath is false. Else the 
        // fullPAth is added
        internal static string PropertiesToUserString(IEnumerable members, bool fullPath) 
        { 
            bool isFirst = true;
            StringBuilder builder = new StringBuilder(); 
            foreach (MemberPath path in members)
            {
                if (isFirst == false)
                { 
                    builder.Append(", ");
                } 
                isFirst = false; 
                if (fullPath)
                { 
                    builder.Append(path.PathToString(false));
                }
                else
                { 
                    builder.Append(path.LastComponentName);
                } 
            } 
            return builder.ToString();
        } 

        // effects: Given a member path and an alias, returns a string
        // correspondng to the fully-qualified name tableAlias.path
        // e.g.,  T1.Address.Phone.Zip. If a subcomponent belongs to 
        // subclass, generates a treat for it, e.g. TREAT(T1 as Customer).Address
        // Or even TREAT(TREAT(T1 AS Customer).Address as USAddress).Zip 
        // Returns the modified builder 
        internal StringBuilder AsCql(StringBuilder inputBuilder, string blockAlias)
        { 
            // Due to the TREAT stuff, we cannot build incrementally. So we use a local
            // StringBuilder -- it should not be that inefficient (one extra copy)

            StringBuilder builder = new StringBuilder(); 
            CqlWriter.AppendEscapedName(builder, blockAlias);
            // Keep track of the previous type so that we can determine if we 
            // need to cast or not 
            EdmType prevType = m_extent.ElementType;
 
            foreach (EdmMember member in m_path)
            {

                // If prevType is a ref (e.g., ref to CPerson), we need to get the 
                // type that it is pointing to and then look for this member in that
 
                StructuralType prevStructuralType; 
                RefType refType = null;
                if (Helper.IsRefType(prevType)) 
                {
                    refType = (RefType)prevType;
                    prevStructuralType = refType.ElementType;
                } 
                else
                { 
                    prevStructuralType = (StructuralType)prevType; 
                }
 
                // Check whether the prevType has the present member in it.
                // If not, we will need to cast the prev type to the
                // appropriate subtype
                bool found = MetadataHelper.DoesMemberExist(prevStructuralType, member); 

                // For reference types, the key must be present in the element type itself. 
                // E.g., if we have Ref(CPerson), the key must be present as CPerson.pid 
                // or CPerson.Address.Phone.Number (i.e., in a complex type). Note that it cannot
                // be present in the subtype of address or phone either, i.e., this path 
                // better not have any TREAT calls
                // We are at CPerson right now. So if we say Key(CPerson), we will get a row with
                // all the key elements. Then we can continue going down the path in CPerson
                if (refType != null) 
                {
                    Debug.Assert(found == true, "We did not find the key property in " + 
                                 "a ref's element type - it cannot be in a subtype"); 
                    // Emit an extract key from the ref
                    builder.Insert(0, "Key("); 
                    builder.Append(")");
                }
                else if (false == found)
                { 
                    // Need to add Treat(... as ...) expression in the
                    // beginning. Note that it does handle cases like 
                    // TREAT(TREAT(T1 AS Customer).Address as USAddress).Zip 
                    Debug.Assert(!Helper.IsRefType(prevType), "We do not allow subtyping in key extraction from Refs");
                    builder.Insert(0, "TREAT("); 
                    builder.Append(" AS ");
                    CqlWriter.AppendEscapedTypeName(builder, member.DeclaringType);
                    builder.Append(')');
                } 

                // Add the member's name. We had a path "T1.A.B" till now 
                builder.Append('.'); 
                CqlWriter.AppendEscapedName(builder, member.Name);
                prevType = member.TypeUsage.EdmType; 
            }
            inputBuilder.Append(builder.ToString());
            return inputBuilder;
        } 

        public bool Equals(MemberPath right) 
        { 
            return EqualityComparer.Equals(this, right);
        } 

        public override bool Equals(object obj)
        {
            MemberPath right = obj as MemberPath; 
            if (obj == null)
            { 
                return false; 
            }
            return Equals(right); 
        }

        public override int GetHashCode()
        { 
            return EqualityComparer.GetHashCode(this);
        } 
 
        // effects: Returns true if the element denoted by the path corresponds to a scalar (primitive or enum)
        internal bool IsScalarType() 
        {
            return EdmType.BuiltInTypeKind == BuiltInTypeKind.PrimitiveType ||
                   EdmType.BuiltInTypeKind == BuiltInTypeKind.EnumType;
        } 

        internal static IEnumerable GetKeyMembers(EntitySetBase extent, MemberDomainMap domainMap, MetadataWorkspace workspace) 
        { 
            MemberPath extentPath = new MemberPath(extent, workspace);
            List keyAttributes = new List( 
                extentPath.GetMembers(extentPath.Extent.ElementType, null /* isScalar */,  null /* isConditional */, true /* isPartOfKey */, domainMap));
            Debug.Assert(keyAttributes.Any(), "No key attributes?");
            return keyAttributes;
        } 

        internal IEnumerable GetMembers(EdmType edmType, bool? isScalar, bool? isConditional, bool? isPartOfKey, MemberDomainMap domainMap) 
        { 
            MemberPath currentPath = this;
            StructuralType structuralType = (StructuralType)edmType; 
            foreach (EdmMember edmMember in structuralType.Members)
            {
                if (edmMember is AssociationEndMember)
                { 
                    // get end's keys
                    foreach (MemberPath endKey in new MemberPath(currentPath, edmMember).GetMembers( 
                                                         ((RefType)edmMember.TypeUsage.EdmType).ElementType, 
                                                         isScalar, isConditional, true /*isPartOfKey*/, domainMap))
                    { 
                        yield return endKey;
                    }
                }
                bool isActuallyScalar = MetadataHelper.IsNonRefSimpleMember(edmMember); 
                if (isScalar == null || isScalar == isActuallyScalar)
                { 
                    EdmProperty childProperty = edmMember as EdmProperty; 
                    if (childProperty != null)
                    { 
                        bool isActuallyKey = MetadataHelper.IsPartOfEntityTypeKey(childProperty);
                        if (isPartOfKey == null || isPartOfKey == isActuallyKey)
                        {
                            MemberPath childPath = new MemberPath(currentPath, childProperty); 
                            bool isActuallyConditional = domainMap.IsConditionMember(childPath);
                            if (isConditional == null || isConditional == isActuallyConditional) 
                            { 
                                yield return childPath;
                            } 
                        }
                    }
                }
            } 
        }
 
        // effects: Returns true if this and path1 are equivalent on the C-side via a referential constraint 
        internal bool IsEquivalentViaRefConstraint(MemberPath path1)
        { 
            MemberPath path0 = this;

            // Now check if they are equivalent via referential constraint
 
            // For example,
            // * Person.pid and PersonAddress.Person.pid are equivalent 
            // * Person.pid and PersonAddress.Address.pid are equivalent 
            // * Person.pid and Address.pid are equivalent if there is a referential constraint
            // * PersonAddress.Person.pid and PersonAddress.Address.pid are 
            //   equivalent if there is a referential constraint

            // In short, Person.pid, Address.pid, PersonAddress.Address.pid,
            // PersonAddress.Person.pid are the same 

            if (path0.EdmType is EntityTypeBase || path1.EdmType is EntityTypeBase || 
                MetadataHelper.IsNonRefSimpleMember(path0.LastMember) == false || 
                MetadataHelper.IsNonRefSimpleMember(path1.LastMember) == false)
            { 
                // If the path corresponds to a top level extent only, ignore
                // it. Or if it is not a scalar
                return false;
            } 

            AssociationSet assocSet0 = path0.Extent as AssociationSet; 
            AssociationSet assocSet1 = path1.Extent as AssociationSet; 
            EntitySet entitySet0 = path0.Extent as EntitySet;
            EntitySet entitySet1 = path1.Extent as EntitySet; 
            bool result = false;

            if (assocSet0 != null && assocSet1 != null)
            { 
                // PersonAddress.Person.pid and PersonAddress.Address.pid case
                // Check if they are the same association or not 
                if (assocSet0.Equals(assocSet1) == false) 
                {
                    return false; 
                }
                result = AreAssocationEndPathsEquivalentViaRefConstraint(path0, path1, assocSet0);
            }
            else if (entitySet0 != null && entitySet1 != null) 
            {
                // Person.pid, Address.pid case 
                // Find all the associations between the two sets. If the 
                // fields are equivalent via any association + referential
                // constraint, return true 
                List assocSets = MetadataHelper.GetAssociationsForEntitySets(entitySet0, entitySet1);
                foreach (AssociationSet assocSet in assocSets)
                {
                    // For Person.pid, get PersonAddress.Person.pid or 
                    MemberPath assocEndPath0 = path0.GetCorrespondingAssociationPath(assocSet);
                    MemberPath assocEndPath1 = path1.GetCorrespondingAssociationPath(assocSet); 
                    if (AreAssocationEndPathsEquivalentViaRefConstraint(assocEndPath0, assocEndPath1, assocSet)) 
                    {
                        result = true; 
                        break;
                    }
                }
 
            }
            else 
            { 
                // One of them is an assocSet and the other is an entity set
                AssociationSet assocSet = assocSet0 != null ? assocSet0 : assocSet1; 
                EntitySet entitySet = entitySet0 != null ? entitySet0 : entitySet1;
                Debug.Assert(assocSet != null && entitySet != null,
                             "One set must be association and the other must be entity set");
 
                MemberPath assocEndPathA = path0.Extent is AssociationSet ? path0 : path1;
                MemberPath entityPath = path0.Extent is EntitySet ? path0 : path1; 
                MemberPath assocEndPathB = entityPath.GetCorrespondingAssociationPath(assocSet); 
                if (assocEndPathB == null)
                { 
                    //An EntitySet might participate in multiple AssociationSets
                    //and this might not be the association set that defines the expected referential
                    //constraint
                    //Return false since this does not have any referential constraint specified 
                    result = false;
                } 
                else 
                {
                    result = AreAssocationEndPathsEquivalentViaRefConstraint(assocEndPathA, assocEndPathB, assocSet); 
                }
            }

            return result; 
        }
 
        // requires: path0 and path1 correspond to paths in assocSet 
        // effects: Returns true if assocPath0 and assocPath1 are
        // equivalent via a referential constraint in assocSet 
        private static bool AreAssocationEndPathsEquivalentViaRefConstraint(MemberPath assocPath0,
                                                                            MemberPath assocPath1,
                                                                            AssociationSet assocSet)
        { 
            Debug.Assert(assocPath0.Extent.Equals(assocSet) && assocPath1.Extent.Equals(assocSet),
                         "Extent for paths must be assocSet"); 
 
            AssociationEndMember end0 = assocPath0.FirstMember as AssociationEndMember;
            AssociationEndMember end1 = assocPath1.FirstMember as AssociationEndMember; 
            EdmProperty property0 = assocPath0.LastMember as EdmProperty;
            EdmProperty property1 = assocPath1.LastMember as EdmProperty;

            if (end0 == null || end1 == null || property0 == null || property1 == null) 
            {
                return false; 
            } 

            // Now check if these fields are connected via a referential constraint 
            AssociationType assocType = assocSet.ElementType;
            bool foundConstraint = false;

            foreach (ReferentialConstraint constraint in assocType.ReferentialConstraints) 
            {
                bool isFrom0 = end0.Name == constraint.FromRole.Name && 
                    end1.Name == constraint.ToRole.Name; 
                bool isFrom1 = end1.Name == constraint.FromRole.Name &&
                    end0.Name == constraint.ToRole.Name; 

                if (isFrom0 || isFrom1)
                {
                    // Found an RI for the two sets. Make sure that the properties are at the same ordinal 

                    // isFrom0 is true when end0 corresponds to FromRole and end1 to ToRole 
                    ReadOnlyMetadataCollection properties0 = isFrom0 ? constraint.FromProperties : constraint.ToProperties; 
                    ReadOnlyMetadataCollection properties1 = isFrom0 ? constraint.ToProperties : constraint.FromProperties;
                    int indexForPath0 = properties0.IndexOf(property0); 
                    int indexForPath1 = properties1.IndexOf(property1);
                    if (indexForPath0 == indexForPath1 && indexForPath0 != -1)
                    {
                        foundConstraint = true; 
                        break;
                    } 
                } 
            }
            return foundConstraint; 
        }

        // Note: this need not correspond to a key field of an entity set E and
        // assocSet corresponds to an association whose one end is E 
        //
        // effects: Returns the MemberPath corresponding to that field in the 
        // assocSet. E.g., given Address.pid, returns PersonAddress.Address.pid. 
        // For self-associations, such as ManagerEmployee with referential
        // constraints (and we have [, 
        // ]), given Employee.mid, returns
        // ManagerEmployee.Employee.mid or ManagerEmployee.Manager.mid
        private MemberPath GetCorrespondingAssociationPath(AssociationSet assocSet)
        { 
            Debug.Assert(Extent is EntitySet, "path must correspond to entity");
 
            // Find the end corresponding to the entity set 
            AssociationEndMember end = MetadataHelper.GetSomeEndForEntitySet(assocSet, (EntitySet)m_extent);
            //An EntitySet might participate in multiple AssociationSets 
            //and this might not be the association set that defines the expected referential
            //constraint
            if (end == null)
            { 
                return null;
            } 
            // Create the new members using the end 
            List newMembers = new List();
            newMembers.Add(end); 
            newMembers.AddRange(m_path);
            // The extent is the assocSet
            MemberPath result = new MemberPath(assocSet, newMembers, m_workspace);
            return result; 
        }
 
        // effects: If this identifies a relationship end, return its scope. 
        // Otherwise, returns null.
        internal EntitySet GetScopeOfRelationEnd() 
        {
            if (m_path.Count == 0)
            {
                return null; 
            }
 
            AssociationEndMember relationEndMember = LastMember as AssociationEndMember; 
            if (relationEndMember == null)
            { 
                return null;
            }

            // Yes, it's a reference, determine its entity set refScope 
            AssociationSet associationSet = (AssociationSet)m_extent;
            EntitySet result = MetadataHelper.GetEntitySetAtEnd(associationSet, relationEndMember); 
            return result; 
        }
 
        // effects: Returns a string of the form a.b.c that corresponds to
        // the "this's" path -- This string can be used for tests or
        // localization
        // If forAlias is true, we return a string that is relevant for Cql 
        // aliases, else we return the exact path
        internal string PathToString(bool? forAlias) 
        { 
            StringBuilder builder = new StringBuilder();
 
            if (forAlias != null)
            {
                if (forAlias==true)
                { 
                    // For the 0th entry, we just choose the type of the element in
                    // which the first entry belongs, e.g., if Addr belongs to CCustomer, 
                    // we choose CCustomer and not CPerson. 
                    if (m_path.Count == 0)
                    { 
                        EntityTypeBase type = m_extent.ElementType;
                        return type.Name;
                    }
                    builder.Append(m_path[0].DeclaringType.Name); // Get CCustomer here 
                }
                else 
                { 
                    // Append the extent name
                    builder.Append(m_extent.Name); 
                }
            }

            // Just join the path using "." 
            for (int i = 0; i < m_path.Count; i++)
            { 
                builder.Append('.'); 
                builder.Append(m_path[i].Name);
            } 
            return builder.ToString();
        }

        // effects: Returns a human-readable string corresponding to this 
        internal override void ToCompactString(StringBuilder builder)
        { 
            builder.Append(PathToString(false)); 
        }
 
        internal void ToCompactString(StringBuilder builder, string instanceToken)
        {
            builder.Append(instanceToken+PathToString(null));
        } 

        #endregion 
 
        #region Comparer
        private class MemberPathComparer : IEqualityComparer 
        {

            public bool Equals(MemberPath left, MemberPath right)
            { 
                if (object.ReferenceEquals(left, right))
                { 
                    return true; 
                }
                // One of them is non-null at least. So if the other one is 
                // null, we cannot be equal
                if (left == null || right == null)
                {
                    return false; 
                }
                // Both are non-null at this point 
                // Checks that the paths are equal component-wise 
                if (left.m_extent.Equals(right.m_extent) == false || left.m_path.Count != right.m_path.Count)
                { 
                    return false;
                }

                for (int i = 0; i < left.m_path.Count; i++) 
                {
                    // Comparing MemberMetadata -- can use Equals 
                    if (false == left.m_path[i].Equals(right.m_path[i])) 
                    {
                        return false; 
                    }
                }
                return true;
            } 

            public int GetHashCode(MemberPath key) 
            { 
                int result = key.m_extent.GetHashCode();
                foreach (EdmMember member in key.m_path) 
                {
                    result ^= member.GetHashCode();
                }
                return result; 
            }
        } 
        #endregion 
    }
} 

// 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