1 //------------------------------------------------------------------------------
2 // <copyright file="ColumnMapKeyBuilder.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
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;
19 namespace System.Data.Common.Internal.Materialization
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.
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)
31 internal class ColumnMapKeyBuilder : ColumnMapVisitor<int>
35 private readonly StringBuilder _builder = new StringBuilder();
36 private readonly SpanIndex _spanIndex;
42 private ColumnMapKeyBuilder(SpanIndex spanIndex)
44 _spanIndex = spanIndex;
49 #region "public" surface area
52 /// Returns a string uniquely identifying the given ColumnMap.
54 internal static string GetColumnMapKey(ColumnMap columnMap, SpanIndex spanIndex)
56 ColumnMapKeyBuilder builder = new ColumnMapKeyBuilder(spanIndex);
57 columnMap.Accept(builder, 0);
58 return builder._builder.ToString();
61 internal void Append(string value)
63 _builder.Append(value);
66 internal void Append(string prefix, Type type)
68 Append(prefix, type.AssemblyQualifiedName);
71 internal void Append(string prefix, TypeUsage type)
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
80 InitializerMetadata initializer;
81 if (InitializerMetadata.TryGetInitializerMetadata(type, out initializer))
83 initializer.AppendColumnMapKey(this);
85 Append(prefix, type.EdmType);
89 internal void Append(string prefix, EdmType type)
93 Append(prefix, type.NamespaceName);
94 Append(".", type.Name);
96 if (type.BuiltInTypeKind == BuiltInTypeKind.RowType)
98 if (_spanIndex != null)
101 Dictionary<int, AssociationEndMember> spanMap = _spanIndex.GetSpanMap((RowType)type);
104 string separator = string.Empty;
105 foreach (var pair in spanMap)
108 AppendValue("C", pair.Key);
109 Append(":", pair.Value.DeclaringType);
110 Append(".", pair.Value.Name);
122 #region helper methods
124 private void Append(string prefix, string value)
132 private void Append(string prefix, ColumnMap columnMap)
136 if (null != columnMap)
138 columnMap.Accept(this, 0);
143 private void Append(string prefix, IEnumerable<ColumnMap> elements)
147 if (null != elements)
149 string separator = string.Empty;
150 foreach (ColumnMap element in elements)
152 Append(separator, element);
159 private void Append(string prefix, EntityIdentity entityIdentity)
164 Append(",K", entityIdentity.Keys);
166 SimpleEntityIdentity simple = entityIdentity as SimpleEntityIdentity;
169 Append(",", simple.EntitySet);
173 DiscriminatedEntityIdentity discriminated = (DiscriminatedEntityIdentity)entityIdentity;
174 Append("CM", discriminated.EntitySetColumnMap);
175 foreach (EntitySet entitySet in discriminated.EntitySetMap)
177 Append(",E", entitySet);
184 private void Append(string prefix, EntitySet entitySet)
186 if (null != entitySet)
188 Append(prefix, entitySet.EntityContainer.Name);
189 Append(".", entitySet.Name);
193 private void AppendValue(string prefix, object value)
195 Append(prefix, String.Format(CultureInfo.InvariantCulture, "{0}", value));
200 #region visitor methods
202 internal override void Visit(ComplexTypeColumnMap columnMap, int dummy)
204 Append("C-", columnMap.Type);
205 Append(",N", columnMap.NullSentinel);
206 Append(",P", columnMap.Properties);
209 internal override void Visit(DiscriminatedCollectionColumnMap columnMap, int dummy)
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);
218 internal override void Visit(EntityColumnMap columnMap, int dummy)
220 Append("E-", columnMap.Type);
221 Append(",N", columnMap.NullSentinel);
222 Append(",P", columnMap.Properties);
223 Append(",I", columnMap.EntityIdentity);
226 internal override void Visit(SimplePolymorphicColumnMap columnMap, int dummy)
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)
234 AppendValue(",K", typeChoice.Key);
235 Append(":", typeChoice.Value);
239 internal override void Visit(RecordColumnMap columnMap, int dummy)
241 Append("R-", columnMap.Type);
242 Append(",N", columnMap.NullSentinel);
243 Append(",P", columnMap.Properties);
246 internal override void Visit(RefColumnMap columnMap, int dummy)
248 Append("Ref-", columnMap.EntityIdentity);
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);
256 internal override void Visit(ScalarColumnMap columnMap, int dummy)
258 String description = String.Format(CultureInfo.InvariantCulture,
259 "S({0}-{1}:{2})", columnMap.CommandId, columnMap.ColumnPos, columnMap.Type.Identity);
263 internal override void Visit(SimpleCollectionColumnMap columnMap, int dummy)
265 Append("DC-FK", columnMap.ForeignKeys);
266 Append(",K", columnMap.Keys);
267 Append(",E", columnMap.Element);
270 internal override void Visit(VarRefColumnMap columnMap, int dummy)
272 Debug.Fail("must not encounter VarRef in ColumnMap for key (eliminated in final ColumnMap)");
275 internal override void Visit(MultipleDiscriminatorPolymorphicColumnMap columnMap, int dummy)
277 // MultipleDiscriminator maps contain an opaque discriminator delegate, so recompilation
278 // is always required. Generate a unique key for the discriminator.
280 Append(String.Format(CultureInfo.InvariantCulture, "MD-{0}", Guid.NewGuid()));