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 IListMembers { 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 IListMembers { 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
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- LongMinMaxAggregationOperator.cs
- SqlUserDefinedAggregateAttribute.cs
- RegexFCD.cs
- ServiceBusyException.cs
- PenContexts.cs
- Configuration.cs
- SystemGatewayIPAddressInformation.cs
- WebRequestModulesSection.cs
- ApplicationDirectory.cs
- GridViewColumn.cs
- MimeMultiPart.cs
- sortedlist.cs
- NavigationPropertySingletonExpression.cs
- Int16Animation.cs
- WindowsFormsHostPropertyMap.cs
- RegistrySecurity.cs
- AssertUtility.cs
- HostingEnvironmentWrapper.cs
- Formatter.cs
- PropertyEmitter.cs
- WorkflowRuntimeElement.cs
- JsonFormatMapping.cs
- RegexRunner.cs
- ThemeableAttribute.cs
- PageThemeBuildProvider.cs
- StringArrayConverter.cs
- SendMailErrorEventArgs.cs
- assemblycache.cs
- TextParentUndoUnit.cs
- DataControlPagerLinkButton.cs
- KernelTypeValidation.cs
- HttpCapabilitiesBase.cs
- MessageSecurityException.cs
- TraceHandlerErrorFormatter.cs
- FormsIdentity.cs
- ToolBarButtonClickEvent.cs
- DetailsViewDeletedEventArgs.cs
- XmlQueryContext.cs
- ResourceFallbackManager.cs
- InternalConfigSettingsFactory.cs
- DbConnectionInternal.cs
- WebResourceUtil.cs
- ProviderCommandInfoUtils.cs
- MissingSatelliteAssemblyException.cs
- XmlDataSource.cs
- DetailsViewDeleteEventArgs.cs
- CompilerError.cs
- Application.cs
- TemplatedWizardStep.cs
- log.cs
- ZipFileInfo.cs
- ReadContentAsBinaryHelper.cs
- HttpModuleAction.cs
- SystemWebCachingSectionGroup.cs
- ConfigurationManagerInternal.cs
- ExtensibleClassFactory.cs
- DataBinder.cs
- TextTreeDeleteContentUndoUnit.cs
- GenericEnumerator.cs
- IOThreadScheduler.cs
- VSDExceptions.cs
- MsmqBindingBase.cs
- VBCodeProvider.cs
- Win32Exception.cs
- Context.cs
- TemplateControlCodeDomTreeGenerator.cs
- FilterEventArgs.cs
- ServiceContractGenerator.cs
- FormatSelectingMessageInspector.cs
- ImageAutomationPeer.cs
- TypeToken.cs
- ConnectionManagementElementCollection.cs
- HandlerFactoryCache.cs
- FontNameConverter.cs
- PDBReader.cs
- ErrorFormatterPage.cs
- DynamicDataRouteHandler.cs
- ControlCollection.cs
- MenuItemBindingCollection.cs
- ObjectSet.cs
- TypeToken.cs
- TreeViewImageKeyConverter.cs
- TransformerInfo.cs
- XsdValidatingReader.cs
- PersonalizationStateQuery.cs
- ResourceDisplayNameAttribute.cs
- GeneralTransform.cs
- DiscoveryService.cs
- AnchorEditor.cs
- DbParameterHelper.cs
- WMIInterop.cs
- PixelFormats.cs
- ResourceKey.cs
- ClusterSafeNativeMethods.cs
- AnimationStorage.cs
- ProfileService.cs
- SkinBuilder.cs
- SoapAttributeAttribute.cs
- RootContext.cs
- NavigationEventArgs.cs