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