1 //--------------------------------------------------------------------- 2 // <copyright file="RelPropertyHelper.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // 6 // @owner Microsoft 7 // @backupOwner Microsoft 8 //--------------------------------------------------------------------- 9 10 using System; 11 using System.Collections.Generic; 12 using System.Globalization; 13 using System.Diagnostics; 14 using System.Data.Common; 15 using System.Data.Metadata.Edm; 16 17 namespace System.Data.Query.InternalTrees 18 { 19 20 /// <summary> 21 /// A "Rel" property is best thought of as a collocated reference (aka foreign key). 22 /// Any entity may have zero or more rel-properties carried along with it (purely 23 /// as a means to optimize for common relationship traversal scenarios) 24 /// 25 /// Although the definition is lax here, we only deal with RelProperties that 26 /// are one-ended (ie) the target multiplicity is at most One. 27 /// 28 /// Consider for example, an Order entity with a (N:1) Order-Customer relationship. The Customer ref 29 /// will be treated as a rel property for the Order entity. 30 /// Similarly, the OrderLine entity may have an Order ref rel property (assuming that there was 31 /// a N:1 relationship between OrderLine and Order) 32 /// </summary> 33 internal sealed class RelProperty 34 { 35 #region private state 36 private readonly RelationshipType m_relationshipType; 37 private readonly RelationshipEndMember m_fromEnd; 38 private readonly RelationshipEndMember m_toEnd; 39 #endregion 40 41 #region constructors RelProperty(RelationshipType relationshipType, RelationshipEndMember fromEnd, RelationshipEndMember toEnd)42 internal RelProperty(RelationshipType relationshipType, RelationshipEndMember fromEnd, RelationshipEndMember toEnd) 43 { 44 m_relationshipType = relationshipType; 45 m_fromEnd = fromEnd; 46 m_toEnd = toEnd; 47 } 48 #endregion 49 50 #region public APIs 51 /// <summary> 52 /// The relationship 53 /// </summary> 54 public RelationshipType Relationship { get { return m_relationshipType; } } 55 56 /// <summary> 57 /// The source end of the relationship 58 /// </summary> 59 public RelationshipEndMember FromEnd { get { return m_fromEnd; } } 60 61 /// <summary> 62 /// the target end of the relationship 63 /// </summary> 64 public RelationshipEndMember ToEnd { get { return m_toEnd; } } 65 66 /// <summary> 67 /// Our definition of equality 68 /// </summary> 69 /// <param name="obj"></param> 70 /// <returns></returns> Equals(object obj)71 public override bool Equals(object obj) 72 { 73 RelProperty other = obj as RelProperty; 74 return (other != null && 75 this.Relationship.EdmEquals(other.Relationship) && 76 this.FromEnd.EdmEquals(other.FromEnd) && 77 this.ToEnd.EdmEquals(other.ToEnd)); 78 } 79 80 /// <summary> 81 /// our hash code 82 /// </summary> 83 /// <returns></returns> GetHashCode()84 public override int GetHashCode() 85 { 86 return this.ToEnd.Identity.GetHashCode(); 87 } 88 89 /// <summary> 90 /// String form 91 /// </summary> 92 /// <returns></returns> 93 [DebuggerNonUserCode] ToString()94 public override string ToString() 95 { 96 return m_relationshipType.ToString() + ":" + 97 m_fromEnd.ToString() + ":" + 98 m_toEnd.ToString(); 99 } 100 101 #endregion 102 } 103 104 /// <summary> 105 /// A helper class for all rel-properties 106 /// </summary> 107 internal sealed class RelPropertyHelper 108 { 109 #region private state 110 private Dictionary<EntityTypeBase, List<RelProperty>> _relPropertyMap; 111 private HashSet<RelProperty> _interestingRelProperties; 112 #endregion 113 114 #region private methods 115 /// <summary> 116 /// Add the rel property induced by the specified relationship, (if the target 117 /// end has a multiplicity of one) 118 /// We only keep track of rel-properties that are "interesting" 119 /// </summary> 120 /// <param name="associationType">the association relationship</param> 121 /// <param name="fromEnd">source end of the relationship traversal</param> 122 /// <param name="toEnd">target end of the traversal</param> AddRelProperty(AssociationType associationType, AssociationEndMember fromEnd, AssociationEndMember toEnd)123 private void AddRelProperty(AssociationType associationType, 124 AssociationEndMember fromEnd, AssociationEndMember toEnd) 125 { 126 if (toEnd.RelationshipMultiplicity == RelationshipMultiplicity.Many) 127 { 128 return; 129 } 130 RelProperty prop = new RelProperty(associationType, fromEnd, toEnd); 131 if (_interestingRelProperties == null || 132 !_interestingRelProperties.Contains(prop)) 133 { 134 return; 135 } 136 137 EntityTypeBase entityType = (EntityTypeBase)((RefType)fromEnd.TypeUsage.EdmType).ElementType; 138 List<RelProperty> propList; 139 if (!_relPropertyMap.TryGetValue(entityType, out propList)) 140 { 141 propList = new List<RelProperty>(); 142 _relPropertyMap[entityType] = propList; 143 } 144 propList.Add(prop); 145 } 146 147 /// <summary> 148 /// Add any rel properties that are induced by the supplied relationship 149 /// </summary> 150 /// <param name="relationshipType">the relationship</param> ProcessRelationship(RelationshipType relationshipType)151 private void ProcessRelationship(RelationshipType relationshipType) 152 { 153 AssociationType associationType = relationshipType as AssociationType; 154 if (associationType == null) 155 { 156 return; 157 } 158 159 // Handle only binary associations 160 if (associationType.AssociationEndMembers.Count != 2) 161 { 162 return; 163 } 164 165 AssociationEndMember end0 = associationType.AssociationEndMembers[0]; 166 AssociationEndMember end1 = associationType.AssociationEndMembers[1]; 167 168 AddRelProperty(associationType, end0, end1); 169 AddRelProperty(associationType, end1, end0); 170 } 171 172 #endregion 173 174 #region constructors RelPropertyHelper(MetadataWorkspace ws, HashSet<RelProperty> interestingRelProperties)175 internal RelPropertyHelper(MetadataWorkspace ws, HashSet<RelProperty> interestingRelProperties) 176 { 177 _relPropertyMap = new Dictionary<EntityTypeBase, List<RelProperty>>(); 178 _interestingRelProperties = interestingRelProperties; 179 180 foreach (RelationshipType relationshipType in ws.GetItems<RelationshipType>(DataSpace.CSpace)) 181 { 182 ProcessRelationship(relationshipType); 183 } 184 } 185 #endregion 186 187 #region public APIs 188 189 /// <summary> 190 /// Get the rel properties declared by this type (and *not* by any of its subtypes) 191 /// </summary> 192 /// <param name="entityType">the entity type</param> 193 /// <returns>set of rel properties declared for this type</returns> GetDeclaredOnlyRelProperties(EntityTypeBase entityType)194 internal IEnumerable<RelProperty> GetDeclaredOnlyRelProperties(EntityTypeBase entityType) 195 { 196 List<RelProperty> relProperties; 197 if (_relPropertyMap.TryGetValue(entityType, out relProperties)) 198 { 199 foreach (RelProperty p in relProperties) 200 { 201 yield return p; 202 } 203 } 204 yield break; 205 } 206 207 /// <summary> 208 /// Get the rel-properties of this entity and its supertypes (starting from the root) 209 /// </summary> 210 /// <param name="entityType">the entity type</param> 211 /// <returns>set of rel-properties for this entity type (and its supertypes)</returns> GetRelProperties(EntityTypeBase entityType)212 internal IEnumerable<RelProperty> GetRelProperties(EntityTypeBase entityType) 213 { 214 if (entityType.BaseType != null) 215 { 216 foreach (RelProperty p in GetRelProperties(entityType.BaseType as EntityTypeBase)) 217 { 218 yield return p; 219 } 220 } 221 222 foreach (RelProperty p in GetDeclaredOnlyRelProperties(entityType)) 223 { 224 yield return p; 225 } 226 227 } 228 229 #endregion 230 } 231 232 } 233