Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Common / Internal / Materialization / ColumnMapKeyBuilder.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="ColumnMapKeyBuilder.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
8
9 using System.Collections.Generic;
10 using System.Data.Metadata.Edm;
11 using System.Data.Objects.ELinq;
12 using System.Data.Objects.Internal;
13 using System.Data.Query.InternalTrees;
14 using System.Diagnostics;
15 using System.Globalization;
16 using System.Linq;
17 using System.Text;
18
19 namespace System.Data.Common.Internal.Materialization
20 {
21     /// <summary>
22     /// Supports building a unique key for a column map so that compiled delegates (<see cref="ShaperFactory"/>)
23     /// can be cached. The general rule: if the <see cref="Translator"/> cares about some property of
24     /// the column map, the generated key must include that property value.
25     /// </summary>
26     /// <remarks>
27     /// IMPORTANT:
28     /// The "X-" prefixes introduced in the different column map types should be unique. This avoids
29     /// conflicts for different column maps with similar properties (e.g. ComplexType and EntityType)
30     /// </remarks>
31     internal class ColumnMapKeyBuilder : ColumnMapVisitor<int>
32     {
33         #region private state
34
35         private readonly StringBuilder _builder = new StringBuilder();
36         private readonly SpanIndex _spanIndex;
37
38         #endregion
39
40         #region constructor
41
42         private ColumnMapKeyBuilder(SpanIndex spanIndex)
43         {
44             _spanIndex = spanIndex;
45         }
46
47         #endregion
48
49         #region "public" surface area
50
51         /// <summary>
52         /// Returns a string uniquely identifying the given ColumnMap.
53         /// </summary>
54         internal static string GetColumnMapKey(ColumnMap columnMap, SpanIndex spanIndex)
55         {
56             ColumnMapKeyBuilder builder = new ColumnMapKeyBuilder(spanIndex);
57             columnMap.Accept(builder, 0);
58             return builder._builder.ToString();
59         }
60
61         internal void Append(string value)
62         {
63             _builder.Append(value);
64         }
65
66         internal void Append(string prefix, Type type)
67         {
68             Append(prefix, type.AssemblyQualifiedName);
69         }
70
71         internal void Append(string prefix, TypeUsage type)
72         {
73             if (null != type)
74             {
75                 // LINQ has anonymous types that aren't going to show up in our
76                 // metadata workspace, and we don't want to hydrate a record when
77                 // we need an anonymous type.  LINQ solves this by annotating the
78                 // edmType with some additional information, which we'll pick up 
79                 // here.
80                 InitializerMetadata initializer;
81                 if (InitializerMetadata.TryGetInitializerMetadata(type, out initializer))
82                 {
83                     initializer.AppendColumnMapKey(this);
84                 }
85                 Append(prefix, type.EdmType);
86             }
87         }
88
89         internal void Append(string prefix, EdmType type)
90         {
91             if (null != type)
92             {
93                 Append(prefix, type.NamespaceName);
94                 Append(".", type.Name);
95
96                 if (type.BuiltInTypeKind == BuiltInTypeKind.RowType)
97                 {
98                     if (_spanIndex != null)
99                     {
100                         Append("<<");
101                         Dictionary<int, AssociationEndMember> spanMap = _spanIndex.GetSpanMap((RowType)type);
102                         if (null != spanMap)
103                         {
104                             string separator = string.Empty;
105                             foreach (var pair in spanMap)
106                             {
107                                 Append(separator);
108                                 AppendValue("C", pair.Key);
109                                 Append(":", pair.Value.DeclaringType);
110                                 Append(".", pair.Value.Name);
111                                 separator = ",";
112                             }
113                         }
114                         Append(">>");
115                     }
116                 }
117             }
118         }
119
120         #endregion
121
122         #region helper methods
123
124         private void Append(string prefix, string value)
125         {
126             Append(prefix);
127             Append("'");
128             Append(value);
129             Append("'");
130         }
131
132         private void Append(string prefix, ColumnMap columnMap)
133         {
134             Append(prefix);
135             Append("[");
136             if (null != columnMap)
137             {
138                 columnMap.Accept(this, 0);
139             }
140             Append("]");
141         }
142
143         private void Append(string prefix, IEnumerable<ColumnMap> elements)
144         {
145             Append(prefix);
146             Append("{");
147             if (null != elements)
148             {
149                 string separator = string.Empty;
150                 foreach (ColumnMap element in elements)
151                 {
152                     Append(separator, element);
153                     separator = ",";
154                 }
155             }
156             Append("}");
157         }
158
159         private void Append(string prefix, EntityIdentity entityIdentity)
160         {
161             Append(prefix);
162             Append("[");
163
164             Append(",K", entityIdentity.Keys);
165
166             SimpleEntityIdentity simple = entityIdentity as SimpleEntityIdentity;
167             if (null != simple)
168             {
169                 Append(",", simple.EntitySet);
170             }
171             else
172             {
173                 DiscriminatedEntityIdentity discriminated = (DiscriminatedEntityIdentity)entityIdentity;
174                 Append("CM", discriminated.EntitySetColumnMap);
175                 foreach (EntitySet entitySet in discriminated.EntitySetMap)
176                 {
177                     Append(",E", entitySet);
178                 }
179             }
180
181             Append("]");
182         }
183
184         private void Append(string prefix, EntitySet entitySet)
185         {
186             if (null != entitySet)
187             {
188                 Append(prefix, entitySet.EntityContainer.Name);
189                 Append(".", entitySet.Name);
190             }
191         }
192
193         private void AppendValue(string prefix, object value)
194         {
195             Append(prefix, String.Format(CultureInfo.InvariantCulture, "{0}", value));
196         }
197
198         #endregion
199
200         #region visitor methods
201
202         internal override void Visit(ComplexTypeColumnMap columnMap, int dummy)
203         {
204             Append("C-", columnMap.Type);
205             Append(",N", columnMap.NullSentinel);
206             Append(",P", columnMap.Properties);
207         }
208
209         internal override void Visit(DiscriminatedCollectionColumnMap columnMap, int dummy)
210         {
211             Append("DC-D", columnMap.Discriminator);
212             AppendValue(",DV", columnMap.DiscriminatorValue);
213             Append(",FK", columnMap.ForeignKeys);
214             Append(",K", columnMap.Keys);
215             Append(",E", columnMap.Element);
216         }
217
218         internal override void Visit(EntityColumnMap columnMap, int dummy)
219         {
220             Append("E-", columnMap.Type);
221             Append(",N", columnMap.NullSentinel);
222             Append(",P", columnMap.Properties);
223             Append(",I", columnMap.EntityIdentity);
224         }
225
226         internal override void Visit(SimplePolymorphicColumnMap columnMap, int dummy)
227         {
228             Append("SP-", columnMap.Type);
229             Append(",D", columnMap.TypeDiscriminator);
230             Append(",N", columnMap.NullSentinel);
231             Append(",P", columnMap.Properties);
232             foreach (var typeChoice in columnMap.TypeChoices)
233             {
234                 AppendValue(",K", typeChoice.Key);
235                 Append(":", typeChoice.Value);
236             }
237         }
238
239         internal override void Visit(RecordColumnMap columnMap, int dummy)
240         {
241             Append("R-", columnMap.Type);
242             Append(",N", columnMap.NullSentinel);
243             Append(",P", columnMap.Properties);
244         }
245
246         internal override void Visit(RefColumnMap columnMap, int dummy)
247         {
248             Append("Ref-", columnMap.EntityIdentity);
249
250             EntityType referencedEntityType;
251             bool isRefType = TypeHelpers.TryGetRefEntityType(columnMap.Type, out referencedEntityType);
252             Debug.Assert(isRefType, "RefColumnMap is not of RefType?");
253             Append(",T", referencedEntityType);
254         }
255
256         internal override void Visit(ScalarColumnMap columnMap, int dummy)
257         {
258             String description = String.Format(CultureInfo.InvariantCulture,
259                                             "S({0}-{1}:{2})", columnMap.CommandId, columnMap.ColumnPos, columnMap.Type.Identity);
260             Append(description);
261         }
262
263         internal override void Visit(SimpleCollectionColumnMap columnMap, int dummy)
264         {
265             Append("DC-FK", columnMap.ForeignKeys);
266             Append(",K", columnMap.Keys);
267             Append(",E", columnMap.Element);
268         }
269
270         internal override void Visit(VarRefColumnMap columnMap, int dummy)
271         {
272             Debug.Fail("must not encounter VarRef in ColumnMap for key (eliminated in final ColumnMap)");
273         }
274
275         internal override void Visit(MultipleDiscriminatorPolymorphicColumnMap columnMap, int dummy)
276         {
277             // MultipleDiscriminator maps contain an opaque discriminator delegate, so recompilation
278             // is always required. Generate a unique key for the discriminator.
279             // 
280             Append(String.Format(CultureInfo.InvariantCulture, "MD-{0}", Guid.NewGuid()));
281         }
282     
283         #endregion
284     }
285 }