1 //---------------------------------------------------------------------
2 // <copyright file="MetadataMappingHasherVisitor.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
11 using System.Collections.Generic;
14 using System.Data.Metadata.Edm;
15 using System.Data.Common;
16 using System.Data.Common.Utils;
17 using System.Data.Mapping;
18 using System.Diagnostics;
19 using System.Globalization;
22 namespace System.Data.Mapping
24 internal partial class MetadataMappingHasherVisitor : BaseMetadataMappingVisitor
26 private CompressingHashBuilder m_hashSourceBuilder;
27 private Dictionary<Object, int> m_itemsAlreadySeen = new Dictionary<Object, int>();
28 private int m_instanceNumber = 0;
29 private EdmItemCollection m_EdmItemCollection;
30 private double m_EdmVersion;
31 private double m_MappingVersion;
33 private MetadataMappingHasherVisitor(double mappingVersion)
35 m_MappingVersion = mappingVersion;
36 this.m_hashSourceBuilder = new CompressingHashBuilder(MetadataHelper.CreateMetadataHashAlgorithm(m_MappingVersion));
39 #region visitor method
40 protected override void Visit(StorageEntityContainerMapping storageEntityContainerMapping)
42 Debug.Assert(storageEntityContainerMapping != null, "storageEntityContainerMapping cannot be null!");
44 // at the entry point of visitor, we setup the versions
45 Debug.Assert(m_MappingVersion == storageEntityContainerMapping.StorageMappingItemCollection.MappingVersion, "the original version and the mapping collection version are not the same");
46 this.m_MappingVersion = storageEntityContainerMapping.StorageMappingItemCollection.MappingVersion;
47 this.m_EdmVersion = storageEntityContainerMapping.StorageMappingItemCollection.EdmItemCollection.EdmVersion;
49 this.m_EdmItemCollection = storageEntityContainerMapping.StorageMappingItemCollection.EdmItemCollection;
52 if (!this.AddObjectToSeenListAndHashBuilder(storageEntityContainerMapping, out index))
54 // if this has been add to the seen list, then just
57 if (this.m_itemsAlreadySeen.Count > 1)
60 // this means user try another visit over SECM, this is allowed but all the previous visit all lost due to clean
61 // user can visit different SECM objects by using the same visitor to load the SECM object
63 Visit(storageEntityContainerMapping);
67 this.AddObjectStartDumpToHashBuilder(storageEntityContainerMapping, index);
69 #region Inner data visit
71 this.AddObjectContentToHashBuilder(storageEntityContainerMapping.Identity);
73 this.AddV2ObjectContentToHashBuilder(storageEntityContainerMapping.GenerateUpdateViews, this.m_MappingVersion);
75 base.Visit(storageEntityContainerMapping);
79 this.AddObjectEndDumpToHashBuilder();
82 protected override void Visit(EntityContainer entityContainer)
85 if (!this.AddObjectToSeenListAndHashBuilder(entityContainer, out index))
90 this.AddObjectStartDumpToHashBuilder(entityContainer, index);
92 #region Inner data visit
94 this.AddObjectContentToHashBuilder(entityContainer.Identity);
95 // Name is covered by Identity
97 base.Visit(entityContainer);
101 this.AddObjectEndDumpToHashBuilder();
104 protected override void Visit(StorageSetMapping storageSetMapping)
107 if (!this.AddObjectToSeenListAndHashBuilder(storageSetMapping, out index))
112 this.AddObjectStartDumpToHashBuilder(storageSetMapping, index);
114 #region Inner data visit
115 base.Visit(storageSetMapping);
118 this.AddObjectEndDumpToHashBuilder();
121 protected override void Visit(StorageTypeMapping storageTypeMapping)
124 if (!this.AddObjectToSeenListAndHashBuilder(storageTypeMapping, out index))
129 this.AddObjectStartDumpToHashBuilder(storageTypeMapping, index);
131 #region Inner data visit
133 base.Visit(storageTypeMapping);
137 this.AddObjectEndDumpToHashBuilder();
140 protected override void Visit(StorageMappingFragment storageMappingFragment)
143 if (!this.AddObjectToSeenListAndHashBuilder(storageMappingFragment, out index))
148 this.AddObjectStartDumpToHashBuilder(storageMappingFragment, index);
150 #region Inner data visit
152 this.AddV2ObjectContentToHashBuilder(storageMappingFragment.IsSQueryDistinct, this.m_MappingVersion);
154 base.Visit(storageMappingFragment);
158 this.AddObjectEndDumpToHashBuilder();
161 protected override void Visit(StoragePropertyMapping storagePropertyMapping)
163 base.Visit(storagePropertyMapping);
166 protected override void Visit(StorageComplexPropertyMapping storageComplexPropertyMapping)
169 if (!this.AddObjectToSeenListAndHashBuilder(storageComplexPropertyMapping, out index))
174 this.AddObjectStartDumpToHashBuilder(storageComplexPropertyMapping, index);
176 #region Inner data visit
178 base.Visit(storageComplexPropertyMapping);
182 this.AddObjectEndDumpToHashBuilder();
184 protected override void Visit(StorageComplexTypeMapping storageComplexTypeMapping)
187 if (!this.AddObjectToSeenListAndHashBuilder(storageComplexTypeMapping, out index))
192 this.AddObjectStartDumpToHashBuilder(storageComplexTypeMapping, index);
194 #region Inner data visit
196 base.Visit(storageComplexTypeMapping);
200 this.AddObjectEndDumpToHashBuilder();
203 protected override void Visit(StorageConditionPropertyMapping storageConditionPropertyMapping)
206 if (!this.AddObjectToSeenListAndHashBuilder(storageConditionPropertyMapping, out index))
211 this.AddObjectStartDumpToHashBuilder(storageConditionPropertyMapping, index);
213 #region Inner data visit
214 this.AddObjectContentToHashBuilder(storageConditionPropertyMapping.IsNull);
215 this.AddObjectContentToHashBuilder(storageConditionPropertyMapping.Value);
217 base.Visit(storageConditionPropertyMapping);
221 this.AddObjectEndDumpToHashBuilder();
224 protected override void Visit(StorageScalarPropertyMapping storageScalarPropertyMapping)
227 if (!this.AddObjectToSeenListAndHashBuilder(storageScalarPropertyMapping, out index))
232 this.AddObjectStartDumpToHashBuilder(storageScalarPropertyMapping, index);
234 #region Inner data visit
236 base.Visit(storageScalarPropertyMapping);
240 this.AddObjectEndDumpToHashBuilder();
243 protected override void Visit(EntitySetBase entitySetBase)
245 base.Visit(entitySetBase);
248 protected override void Visit(EntitySet entitySet)
251 if (!this.AddObjectToSeenListAndHashBuilder(entitySet, out index))
255 #region Inner data visit
257 this.AddObjectStartDumpToHashBuilder(entitySet, index);
258 this.AddObjectContentToHashBuilder(entitySet.Name);
259 this.AddObjectContentToHashBuilder(entitySet.Schema);
260 this.AddObjectContentToHashBuilder(entitySet.Table);
262 base.Visit(entitySet);
264 foreach (var entityType in MetadataHelper.GetTypeAndSubtypesOf(entitySet.ElementType, this.m_EdmItemCollection, false).Where(type => type != entitySet.ElementType))
266 this.Visit(entityType);
271 this.AddObjectEndDumpToHashBuilder();
274 protected override void Visit(AssociationSet associationSet)
277 if (!this.AddObjectToSeenListAndHashBuilder(associationSet, out index))
282 this.AddObjectStartDumpToHashBuilder(associationSet, index);
284 #region Inner data visit
285 this.AddObjectContentToHashBuilder(associationSet.CachedProviderSql);
286 // Name is coverd by Identity
287 this.AddObjectContentToHashBuilder(associationSet.Identity);
288 this.AddObjectContentToHashBuilder(associationSet.Schema);
289 this.AddObjectContentToHashBuilder(associationSet.Table);
291 base.Visit(associationSet);
295 this.AddObjectEndDumpToHashBuilder();
298 protected override void Visit(EntityType entityType)
301 if (!this.AddObjectToSeenListAndHashBuilder(entityType, out index))
306 this.AddObjectStartDumpToHashBuilder(entityType, index);
308 #region Inner data visit
309 this.AddObjectContentToHashBuilder(entityType.Abstract);
310 this.AddObjectContentToHashBuilder(entityType.Identity);
311 // FullName, Namespace and Name are all covered by Identity
313 base.Visit(entityType);
317 this.AddObjectEndDumpToHashBuilder();
320 protected override void Visit(AssociationSetEnd associationSetEnd)
323 if (!this.AddObjectToSeenListAndHashBuilder(associationSetEnd, out index))
328 this.AddObjectStartDumpToHashBuilder(associationSetEnd, index);
330 #region Inner data visit
331 this.AddObjectContentToHashBuilder(associationSetEnd.Identity);
332 // Name is covered by Identity
334 base.Visit(associationSetEnd);
338 this.AddObjectEndDumpToHashBuilder();
341 protected override void Visit(AssociationType associationType)
344 if (!this.AddObjectToSeenListAndHashBuilder(associationType, out index))
349 this.AddObjectStartDumpToHashBuilder(associationType, index);
351 #region Inner data visit
352 this.AddObjectContentToHashBuilder(associationType.Abstract);
353 this.AddObjectContentToHashBuilder(associationType.Identity);
354 // FullName, Namespace, and Name are all covered by Identity
356 base.Visit(associationType);
360 this.AddObjectEndDumpToHashBuilder();
363 protected override void Visit(EdmProperty edmProperty)
366 if (!this.AddObjectToSeenListAndHashBuilder(edmProperty, out index))
371 this.AddObjectStartDumpToHashBuilder(edmProperty, index);
373 #region Inner data visit
374 // since the delaring type is fixed and referenced to the upper type,
375 // there is no need to hash this
376 //this.AddObjectContentToHashBuilder(edmProperty.DeclaringType);
377 this.AddObjectContentToHashBuilder(edmProperty.DefaultValue);
378 this.AddObjectContentToHashBuilder(edmProperty.Identity);
379 // Name is covered by Identity
380 this.AddObjectContentToHashBuilder(edmProperty.IsStoreGeneratedComputed);
381 this.AddObjectContentToHashBuilder(edmProperty.IsStoreGeneratedIdentity);
382 this.AddObjectContentToHashBuilder(edmProperty.Nullable);
384 base.Visit(edmProperty);
388 this.AddObjectEndDumpToHashBuilder();
391 protected override void Visit(NavigationProperty navigationProperty)
393 // navigation properties are not considered in view generation
397 protected override void Visit(EdmMember edmMember)
400 if (!this.AddObjectToSeenListAndHashBuilder(edmMember, out index))
405 this.AddObjectStartDumpToHashBuilder(edmMember, index);
407 #region Inner data visit
408 this.AddObjectContentToHashBuilder(edmMember.Identity);
409 // Name is covered by Identity
410 this.AddObjectContentToHashBuilder(edmMember.IsStoreGeneratedComputed);
411 this.AddObjectContentToHashBuilder(edmMember.IsStoreGeneratedIdentity);
413 base.Visit(edmMember);
417 this.AddObjectEndDumpToHashBuilder();
420 protected override void Visit(AssociationEndMember associationEndMember)
423 if (!this.AddObjectToSeenListAndHashBuilder(associationEndMember, out index))
428 this.AddObjectStartDumpToHashBuilder(associationEndMember, index);
430 #region Inner data visit
431 this.AddObjectContentToHashBuilder(associationEndMember.DeleteBehavior);
432 this.AddObjectContentToHashBuilder(associationEndMember.Identity);
433 // Name is covered by Identity
434 this.AddObjectContentToHashBuilder(associationEndMember.IsStoreGeneratedComputed);
435 this.AddObjectContentToHashBuilder(associationEndMember.IsStoreGeneratedIdentity);
436 this.AddObjectContentToHashBuilder(associationEndMember.RelationshipMultiplicity);
438 base.Visit(associationEndMember);
442 this.AddObjectEndDumpToHashBuilder();
445 protected override void Visit(ReferentialConstraint referentialConstraint)
448 if (!this.AddObjectToSeenListAndHashBuilder(referentialConstraint, out index))
453 this.AddObjectStartDumpToHashBuilder(referentialConstraint, index);
455 #region Inner data visit
456 this.AddObjectContentToHashBuilder(referentialConstraint.Identity);
458 base.Visit(referentialConstraint);
462 this.AddObjectEndDumpToHashBuilder();
465 protected override void Visit(RelationshipEndMember relationshipEndMember)
468 if (!this.AddObjectToSeenListAndHashBuilder(relationshipEndMember, out index))
473 this.AddObjectStartDumpToHashBuilder(relationshipEndMember, index);
475 #region Inner data visit
476 this.AddObjectContentToHashBuilder(relationshipEndMember.DeleteBehavior);
477 this.AddObjectContentToHashBuilder(relationshipEndMember.Identity);
478 // Name is covered by Identity
479 this.AddObjectContentToHashBuilder(relationshipEndMember.IsStoreGeneratedComputed);
480 this.AddObjectContentToHashBuilder(relationshipEndMember.IsStoreGeneratedIdentity);
481 this.AddObjectContentToHashBuilder(relationshipEndMember.RelationshipMultiplicity);
483 base.Visit(relationshipEndMember);
487 this.AddObjectEndDumpToHashBuilder();
490 protected override void Visit(TypeUsage typeUsage)
493 if (!this.AddObjectToSeenListAndHashBuilder(typeUsage, out index))
498 this.AddObjectStartDumpToHashBuilder(typeUsage, index);
500 #region Inner data visit
501 //No need to add identity of TypeUsage to the hash since it would take into account
502 //facets that viewgen would not care and we visit the important facets anyway.
504 base.Visit(typeUsage);
508 this.AddObjectEndDumpToHashBuilder();
511 protected override void Visit(RelationshipType relationshipType)
513 base.Visit(relationshipType);
516 protected override void Visit(EdmType edmType)
521 protected override void Visit(EnumType enumType)
524 if (!this.AddObjectToSeenListAndHashBuilder(enumType, out index))
529 this.AddObjectStartDumpToHashBuilder(enumType, index);
531 this.AddObjectContentToHashBuilder(enumType.Identity);
532 this.Visit(enumType.UnderlyingType);
534 base.Visit(enumType);
536 this.AddObjectEndDumpToHashBuilder();
539 protected override void Visit(EnumMember enumMember)
542 if (!this.AddObjectToSeenListAndHashBuilder(enumMember, out index))
547 this.AddObjectStartDumpToHashBuilder(enumMember, index);
549 this.AddObjectContentToHashBuilder(enumMember.Name);
550 this.AddObjectContentToHashBuilder(enumMember.Value);
552 base.Visit(enumMember);
554 this.AddObjectEndDumpToHashBuilder();
557 protected override void Visit(CollectionType collectionType)
560 if (!this.AddObjectToSeenListAndHashBuilder(collectionType, out index))
565 this.AddObjectStartDumpToHashBuilder(collectionType, index);
567 #region Inner data visit
568 this.AddObjectContentToHashBuilder(collectionType.Identity);
569 // Identity contains Name, NamespaceName and FullName
571 base.Visit(collectionType);
575 this.AddObjectEndDumpToHashBuilder();
578 protected override void Visit(RefType refType)
581 if (!this.AddObjectToSeenListAndHashBuilder(refType, out index))
586 this.AddObjectStartDumpToHashBuilder(refType, index);
588 #region Inner data visit
589 this.AddObjectContentToHashBuilder(refType.Identity);
590 // Identity contains Name, NamespaceName and FullName
596 this.AddObjectEndDumpToHashBuilder();
599 protected override void Visit(EntityTypeBase entityTypeBase)
601 base.Visit(entityTypeBase);
604 protected override void Visit(Facet facet)
607 if (facet.Name != DbProviderManifest.NullableFacetName)
609 // skip all the non interesting facets
613 if (!this.AddObjectToSeenListAndHashBuilder(facet, out index))
618 this.AddObjectStartDumpToHashBuilder(facet, index);
620 #region Inner data visit
621 this.AddObjectContentToHashBuilder(facet.Identity);
622 // Identity already contains Name
623 this.AddObjectContentToHashBuilder(facet.Value);
629 this.AddObjectEndDumpToHashBuilder();
632 protected override void Visit(EdmFunction edmFunction)
634 // View Generation doesn't deal with functions
638 protected override void Visit(ComplexType complexType)
641 if (!this.AddObjectToSeenListAndHashBuilder(complexType, out index))
646 this.AddObjectStartDumpToHashBuilder(complexType, index);
648 #region Inner data visit
649 this.AddObjectContentToHashBuilder(complexType.Abstract);
650 this.AddObjectContentToHashBuilder(complexType.Identity);
651 // Identity covers, FullName, Name, and NamespaceName
653 base.Visit(complexType);
657 this.AddObjectEndDumpToHashBuilder();
660 protected override void Visit(PrimitiveType primitiveType)
663 if (!this.AddObjectToSeenListAndHashBuilder(primitiveType, out index))
668 this.AddObjectStartDumpToHashBuilder(primitiveType, index);
670 #region Inner data visit
671 this.AddObjectContentToHashBuilder(primitiveType.Name);
672 this.AddObjectContentToHashBuilder(primitiveType.NamespaceName);
674 base.Visit(primitiveType);
678 this.AddObjectEndDumpToHashBuilder();
681 protected override void Visit(FunctionParameter functionParameter)
684 if (!this.AddObjectToSeenListAndHashBuilder(functionParameter, out index))
689 this.AddObjectStartDumpToHashBuilder(functionParameter, index);
691 #region Inner data visit
692 this.AddObjectContentToHashBuilder(functionParameter.Identity);
693 // Identity already has Name
694 this.AddObjectContentToHashBuilder(functionParameter.Mode);
696 base.Visit(functionParameter);
700 this.AddObjectEndDumpToHashBuilder();
703 protected override void Visit(DbProviderManifest providerManifest)
705 // the provider manifest will be checked by all the other types lining up.
706 // no need to store more info.
710 #region hasher helper method
712 internal string HashValue
716 return m_hashSourceBuilder.ComputeHash();
722 this.m_hashSourceBuilder = new CompressingHashBuilder(MetadataHelper.CreateMetadataHashAlgorithm(m_MappingVersion));
723 this.m_instanceNumber = 0;
724 this.m_itemsAlreadySeen = new Dictionary<object, int>();
728 /// if already seen, then out the object instance index, return false;
729 /// if haven't seen, then add it to the m_itemAlreadySeen, out the current index, return true
731 /// <param name="o"></param>
732 /// <param name="indexSeen"></param>
733 /// <returns></returns>
734 private bool TryAddSeenItem(Object o, out int indexSeen)
736 if (!this.m_itemsAlreadySeen.TryGetValue(o, out indexSeen))
738 this.m_itemsAlreadySeen.Add(o, this.m_instanceNumber);
740 indexSeen = this.m_instanceNumber;
741 this.m_instanceNumber++;
749 /// if the object has seen, then add the seen object style to the hash source, return false;
750 /// if not, then add it to the seen list, and append the object start dump to the hash source, return true
752 /// <param name="o"></param>
753 /// <returns></returns>
754 private bool AddObjectToSeenListAndHashBuilder(object o, out int instanceIndex)
761 if (!TryAddSeenItem(o, out instanceIndex))
763 this.AddObjectStartDumpToHashBuilder(o, instanceIndex);
764 this.AddSeenObjectToHashBuilder(o, instanceIndex);
765 this.AddObjectEndDumpToHashBuilder();
771 private void AddSeenObjectToHashBuilder(object o, int instanceIndex)
773 Debug.Assert(instanceIndex >= 0, "referencing index should not be less than 0");
774 this.m_hashSourceBuilder.AppendLine("Instance Reference: " + instanceIndex);
777 private void AddObjectStartDumpToHashBuilder(object o, int objectIndex)
779 this.m_hashSourceBuilder.AppendObjectStartDump(o, objectIndex);
782 private void AddObjectEndDumpToHashBuilder()
784 this.m_hashSourceBuilder.AppendObjectEndDump();
787 private void AddObjectContentToHashBuilder(object content)
791 IFormattable formatContent = content as IFormattable;
792 if (formatContent != null)
794 // if the content is formattable, the following code made it culture invariant,
795 // for instance, the int, "30,000" can be formatted to "30-000" if the user
796 // has a different language and region setting
797 this.m_hashSourceBuilder.AppendLine(formatContent.ToString(null, CultureInfo.InvariantCulture));
801 this.m_hashSourceBuilder.AppendLine(content.ToString());
806 this.m_hashSourceBuilder.AppendLine("NULL");
811 /// Add V2 schema properties and attributes to the hash builder
813 /// <param name="content"></param>
814 /// <param name="defaultValue"></param>
815 private void AddV2ObjectContentToHashBuilder(object content, double version)
817 // if the version number is greater than or equal to V2, then we add the value
818 if (version >= XmlConstants.EdmVersionForV2)
820 this.AddObjectContentToHashBuilder(content);
824 internal static string GetMappingClosureHash(double mappingVersion, StorageEntityContainerMapping storageEntityContainerMapping)
826 Debug.Assert(storageEntityContainerMapping != null, "storageEntityContainerMapping is null!");
828 MetadataMappingHasherVisitor visitor = new MetadataMappingHasherVisitor(mappingVersion);
829 visitor.Visit(storageEntityContainerMapping);
830 return visitor.HashValue;