//--------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // @owner [....] // @backupOwner [....] //--------------------------------------------------------------------- using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data.Metadata.Edm; using System.Data.Common; using System.Data.Common.Utils; using System.Data.Mapping; using System.Diagnostics; using System.Globalization; namespace System.Data.Mapping { internal partial class MetadataMappingHasherVisitor : BaseMetadataMappingVisitor { private CompressingHashBuilder m_hashSourceBuilder; private Dictionary m_itemsAlreadySeen = new Dictionary(); private int m_instanceNumber = 0; private EdmItemCollection m_EdmItemCollection; private double m_EdmVersion; private double m_MappingVersion; private MetadataMappingHasherVisitor(double mappingVersion) { m_MappingVersion = mappingVersion; this.m_hashSourceBuilder = new CompressingHashBuilder(MetadataHelper.CreateMetadataHashAlgorithm(m_MappingVersion)); } #region visitor method protected override void Visit(StorageEntityContainerMapping storageEntityContainerMapping) { Debug.Assert(storageEntityContainerMapping != null, "storageEntityContainerMapping cannot be null!"); // at the entry point of visitor, we setup the versions Debug.Assert(m_MappingVersion == storageEntityContainerMapping.StorageMappingItemCollection.MappingVersion, "the original version and the mapping collection version are not the same"); this.m_MappingVersion = storageEntityContainerMapping.StorageMappingItemCollection.MappingVersion; this.m_EdmVersion = storageEntityContainerMapping.StorageMappingItemCollection.EdmItemCollection.EdmVersion; this.m_EdmItemCollection = storageEntityContainerMapping.StorageMappingItemCollection.EdmItemCollection; int index; if (!this.AddObjectToSeenListAndHashBuilder(storageEntityContainerMapping, out index)) { // if this has been add to the seen list, then just return; } if (this.m_itemsAlreadySeen.Count > 1) { // this means user try another visit over SECM, this is allowed but all the previous visit all lost due to clean // user can visit different SECM objects by using the same visitor to load the SECM object this.Clean(); Visit(storageEntityContainerMapping); return; } this.AddObjectStartDumpToHashBuilder(storageEntityContainerMapping, index); #region Inner data visit this.AddObjectContentToHashBuilder(storageEntityContainerMapping.Identity); this.AddV2ObjectContentToHashBuilder(storageEntityContainerMapping.GenerateUpdateViews, this.m_MappingVersion); base.Visit(storageEntityContainerMapping); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(EntityContainer entityContainer) { int index; if (!this.AddObjectToSeenListAndHashBuilder(entityContainer, out index)) { return; } this.AddObjectStartDumpToHashBuilder(entityContainer, index); #region Inner data visit this.AddObjectContentToHashBuilder(entityContainer.Identity); // Name is covered by Identity base.Visit(entityContainer); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(StorageSetMapping storageSetMapping) { int index; if (!this.AddObjectToSeenListAndHashBuilder(storageSetMapping, out index)) { return; } this.AddObjectStartDumpToHashBuilder(storageSetMapping, index); #region Inner data visit base.Visit(storageSetMapping); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(StorageTypeMapping storageTypeMapping) { int index; if (!this.AddObjectToSeenListAndHashBuilder(storageTypeMapping, out index)) { return; } this.AddObjectStartDumpToHashBuilder(storageTypeMapping, index); #region Inner data visit base.Visit(storageTypeMapping); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(StorageMappingFragment storageMappingFragment) { int index; if (!this.AddObjectToSeenListAndHashBuilder(storageMappingFragment, out index)) { return; } this.AddObjectStartDumpToHashBuilder(storageMappingFragment, index); #region Inner data visit this.AddV2ObjectContentToHashBuilder(storageMappingFragment.IsSQueryDistinct, this.m_MappingVersion); base.Visit(storageMappingFragment); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(StoragePropertyMapping storagePropertyMapping) { base.Visit(storagePropertyMapping); } protected override void Visit(StorageComplexPropertyMapping storageComplexPropertyMapping) { int index; if (!this.AddObjectToSeenListAndHashBuilder(storageComplexPropertyMapping, out index)) { return; } this.AddObjectStartDumpToHashBuilder(storageComplexPropertyMapping, index); #region Inner data visit base.Visit(storageComplexPropertyMapping); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(StorageComplexTypeMapping storageComplexTypeMapping) { int index; if (!this.AddObjectToSeenListAndHashBuilder(storageComplexTypeMapping, out index)) { return; } this.AddObjectStartDumpToHashBuilder(storageComplexTypeMapping, index); #region Inner data visit base.Visit(storageComplexTypeMapping); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(StorageConditionPropertyMapping storageConditionPropertyMapping) { int index; if (!this.AddObjectToSeenListAndHashBuilder(storageConditionPropertyMapping, out index)) { return; } this.AddObjectStartDumpToHashBuilder(storageConditionPropertyMapping, index); #region Inner data visit this.AddObjectContentToHashBuilder(storageConditionPropertyMapping.IsNull); this.AddObjectContentToHashBuilder(storageConditionPropertyMapping.Value); base.Visit(storageConditionPropertyMapping); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(StorageScalarPropertyMapping storageScalarPropertyMapping) { int index; if (!this.AddObjectToSeenListAndHashBuilder(storageScalarPropertyMapping, out index)) { return; } this.AddObjectStartDumpToHashBuilder(storageScalarPropertyMapping, index); #region Inner data visit base.Visit(storageScalarPropertyMapping); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(EntitySetBase entitySetBase) { base.Visit(entitySetBase); } protected override void Visit(EntitySet entitySet) { int index; if (!this.AddObjectToSeenListAndHashBuilder(entitySet, out index)) { return; } #region Inner data visit this.AddObjectStartDumpToHashBuilder(entitySet, index); this.AddObjectContentToHashBuilder(entitySet.Name); this.AddObjectContentToHashBuilder(entitySet.Schema); this.AddObjectContentToHashBuilder(entitySet.Table); base.Visit(entitySet); foreach (var entityType in MetadataHelper.GetTypeAndSubtypesOf(entitySet.ElementType, this.m_EdmItemCollection, false).Where(type => type != entitySet.ElementType)) { this.Visit(entityType); } #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(AssociationSet associationSet) { int index; if (!this.AddObjectToSeenListAndHashBuilder(associationSet, out index)) { return; } this.AddObjectStartDumpToHashBuilder(associationSet, index); #region Inner data visit this.AddObjectContentToHashBuilder(associationSet.CachedProviderSql); // Name is coverd by Identity this.AddObjectContentToHashBuilder(associationSet.Identity); this.AddObjectContentToHashBuilder(associationSet.Schema); this.AddObjectContentToHashBuilder(associationSet.Table); base.Visit(associationSet); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(EntityType entityType) { int index; if (!this.AddObjectToSeenListAndHashBuilder(entityType, out index)) { return; } this.AddObjectStartDumpToHashBuilder(entityType, index); #region Inner data visit this.AddObjectContentToHashBuilder(entityType.Abstract); this.AddObjectContentToHashBuilder(entityType.Identity); // FullName, Namespace and Name are all covered by Identity base.Visit(entityType); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(AssociationSetEnd associationSetEnd) { int index; if (!this.AddObjectToSeenListAndHashBuilder(associationSetEnd, out index)) { return; } this.AddObjectStartDumpToHashBuilder(associationSetEnd, index); #region Inner data visit this.AddObjectContentToHashBuilder(associationSetEnd.Identity); // Name is covered by Identity base.Visit(associationSetEnd); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(AssociationType associationType) { int index; if (!this.AddObjectToSeenListAndHashBuilder(associationType, out index)) { return; } this.AddObjectStartDumpToHashBuilder(associationType, index); #region Inner data visit this.AddObjectContentToHashBuilder(associationType.Abstract); this.AddObjectContentToHashBuilder(associationType.Identity); // FullName, Namespace, and Name are all covered by Identity base.Visit(associationType); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(EdmProperty edmProperty) { int index; if (!this.AddObjectToSeenListAndHashBuilder(edmProperty, out index)) { return; } this.AddObjectStartDumpToHashBuilder(edmProperty, index); #region Inner data visit // since the delaring type is fixed and referenced to the upper type, // there is no need to hash this //this.AddObjectContentToHashBuilder(edmProperty.DeclaringType); this.AddObjectContentToHashBuilder(edmProperty.DefaultValue); this.AddObjectContentToHashBuilder(edmProperty.Identity); // Name is covered by Identity this.AddObjectContentToHashBuilder(edmProperty.IsStoreGeneratedComputed); this.AddObjectContentToHashBuilder(edmProperty.IsStoreGeneratedIdentity); this.AddObjectContentToHashBuilder(edmProperty.Nullable); base.Visit(edmProperty); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(NavigationProperty navigationProperty) { // navigation properties are not considered in view generation return; } protected override void Visit(EdmMember edmMember) { int index; if (!this.AddObjectToSeenListAndHashBuilder(edmMember, out index)) { return; } this.AddObjectStartDumpToHashBuilder(edmMember, index); #region Inner data visit this.AddObjectContentToHashBuilder(edmMember.Identity); // Name is covered by Identity this.AddObjectContentToHashBuilder(edmMember.IsStoreGeneratedComputed); this.AddObjectContentToHashBuilder(edmMember.IsStoreGeneratedIdentity); base.Visit(edmMember); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(AssociationEndMember associationEndMember) { int index; if (!this.AddObjectToSeenListAndHashBuilder(associationEndMember, out index)) { return; } this.AddObjectStartDumpToHashBuilder(associationEndMember, index); #region Inner data visit this.AddObjectContentToHashBuilder(associationEndMember.DeleteBehavior); this.AddObjectContentToHashBuilder(associationEndMember.Identity); // Name is covered by Identity this.AddObjectContentToHashBuilder(associationEndMember.IsStoreGeneratedComputed); this.AddObjectContentToHashBuilder(associationEndMember.IsStoreGeneratedIdentity); this.AddObjectContentToHashBuilder(associationEndMember.RelationshipMultiplicity); base.Visit(associationEndMember); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(ReferentialConstraint referentialConstraint) { int index; if (!this.AddObjectToSeenListAndHashBuilder(referentialConstraint, out index)) { return; } this.AddObjectStartDumpToHashBuilder(referentialConstraint, index); #region Inner data visit this.AddObjectContentToHashBuilder(referentialConstraint.Identity); base.Visit(referentialConstraint); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(RelationshipEndMember relationshipEndMember) { int index; if (!this.AddObjectToSeenListAndHashBuilder(relationshipEndMember, out index)) { return; } this.AddObjectStartDumpToHashBuilder(relationshipEndMember, index); #region Inner data visit this.AddObjectContentToHashBuilder(relationshipEndMember.DeleteBehavior); this.AddObjectContentToHashBuilder(relationshipEndMember.Identity); // Name is covered by Identity this.AddObjectContentToHashBuilder(relationshipEndMember.IsStoreGeneratedComputed); this.AddObjectContentToHashBuilder(relationshipEndMember.IsStoreGeneratedIdentity); this.AddObjectContentToHashBuilder(relationshipEndMember.RelationshipMultiplicity); base.Visit(relationshipEndMember); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(TypeUsage typeUsage) { int index; if (!this.AddObjectToSeenListAndHashBuilder(typeUsage, out index)) { return; } this.AddObjectStartDumpToHashBuilder(typeUsage, index); #region Inner data visit //No need to add identity of TypeUsage to the hash since it would take into account //facets that viewgen would not care and we visit the important facets anyway. base.Visit(typeUsage); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(RelationshipType relationshipType) { base.Visit(relationshipType); } protected override void Visit(EdmType edmType) { base.Visit(edmType); } protected override void Visit(EnumType enumType) { int index; if (!this.AddObjectToSeenListAndHashBuilder(enumType, out index)) { return; } this.AddObjectStartDumpToHashBuilder(enumType, index); this.AddObjectContentToHashBuilder(enumType.Identity); this.Visit(enumType.UnderlyingType); base.Visit(enumType); this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(EnumMember enumMember) { int index; if (!this.AddObjectToSeenListAndHashBuilder(enumMember, out index)) { return; } this.AddObjectStartDumpToHashBuilder(enumMember, index); this.AddObjectContentToHashBuilder(enumMember.Name); this.AddObjectContentToHashBuilder(enumMember.Value); base.Visit(enumMember); this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(CollectionType collectionType) { int index; if (!this.AddObjectToSeenListAndHashBuilder(collectionType, out index)) { return; } this.AddObjectStartDumpToHashBuilder(collectionType, index); #region Inner data visit this.AddObjectContentToHashBuilder(collectionType.Identity); // Identity contains Name, NamespaceName and FullName base.Visit(collectionType); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(RefType refType) { int index; if (!this.AddObjectToSeenListAndHashBuilder(refType, out index)) { return; } this.AddObjectStartDumpToHashBuilder(refType, index); #region Inner data visit this.AddObjectContentToHashBuilder(refType.Identity); // Identity contains Name, NamespaceName and FullName base.Visit(refType); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(EntityTypeBase entityTypeBase) { base.Visit(entityTypeBase); } protected override void Visit(Facet facet) { int index; if (facet.Name != DbProviderManifest.NullableFacetName) { // skip all the non interesting facets return; } if (!this.AddObjectToSeenListAndHashBuilder(facet, out index)) { return; } this.AddObjectStartDumpToHashBuilder(facet, index); #region Inner data visit this.AddObjectContentToHashBuilder(facet.Identity); // Identity already contains Name this.AddObjectContentToHashBuilder(facet.Value); base.Visit(facet); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(EdmFunction edmFunction) { // View Generation doesn't deal with functions // so just return; } protected override void Visit(ComplexType complexType) { int index; if (!this.AddObjectToSeenListAndHashBuilder(complexType, out index)) { return; } this.AddObjectStartDumpToHashBuilder(complexType, index); #region Inner data visit this.AddObjectContentToHashBuilder(complexType.Abstract); this.AddObjectContentToHashBuilder(complexType.Identity); // Identity covers, FullName, Name, and NamespaceName base.Visit(complexType); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(PrimitiveType primitiveType) { int index; if (!this.AddObjectToSeenListAndHashBuilder(primitiveType, out index)) { return; } this.AddObjectStartDumpToHashBuilder(primitiveType, index); #region Inner data visit this.AddObjectContentToHashBuilder(primitiveType.Name); this.AddObjectContentToHashBuilder(primitiveType.NamespaceName); base.Visit(primitiveType); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(FunctionParameter functionParameter) { int index; if (!this.AddObjectToSeenListAndHashBuilder(functionParameter, out index)) { return; } this.AddObjectStartDumpToHashBuilder(functionParameter, index); #region Inner data visit this.AddObjectContentToHashBuilder(functionParameter.Identity); // Identity already has Name this.AddObjectContentToHashBuilder(functionParameter.Mode); base.Visit(functionParameter); #endregion this.AddObjectEndDumpToHashBuilder(); } protected override void Visit(DbProviderManifest providerManifest) { // the provider manifest will be checked by all the other types lining up. // no need to store more info. } #endregion #region hasher helper method internal string HashValue { get { return m_hashSourceBuilder.ComputeHash(); } } private void Clean() { this.m_hashSourceBuilder = new CompressingHashBuilder(MetadataHelper.CreateMetadataHashAlgorithm(m_MappingVersion)); this.m_instanceNumber = 0; this.m_itemsAlreadySeen = new Dictionary(); } /// /// if already seen, then out the object instance index, return false; /// if haven't seen, then add it to the m_itemAlreadySeen, out the current index, return true /// /// /// /// private bool TryAddSeenItem(Object o, out int indexSeen) { if (!this.m_itemsAlreadySeen.TryGetValue(o, out indexSeen)) { this.m_itemsAlreadySeen.Add(o, this.m_instanceNumber); indexSeen = this.m_instanceNumber; this.m_instanceNumber++; return true; } return false; } /// /// if the object has seen, then add the seen object style to the hash source, return false; /// if not, then add it to the seen list, and append the object start dump to the hash source, return true /// /// /// private bool AddObjectToSeenListAndHashBuilder(object o, out int instanceIndex) { if (o == null) { instanceIndex = -1; return false; } if (!TryAddSeenItem(o, out instanceIndex)) { this.AddObjectStartDumpToHashBuilder(o, instanceIndex); this.AddSeenObjectToHashBuilder(o, instanceIndex); this.AddObjectEndDumpToHashBuilder(); return false; } return true; } private void AddSeenObjectToHashBuilder(object o, int instanceIndex) { Debug.Assert(instanceIndex >= 0, "referencing index should not be less than 0"); this.m_hashSourceBuilder.AppendLine("Instance Reference: " + instanceIndex); } private void AddObjectStartDumpToHashBuilder(object o, int objectIndex) { this.m_hashSourceBuilder.AppendObjectStartDump(o, objectIndex); } private void AddObjectEndDumpToHashBuilder() { this.m_hashSourceBuilder.AppendObjectEndDump(); } private void AddObjectContentToHashBuilder(object content) { if (content != null) { IFormattable formatContent = content as IFormattable; if (formatContent != null) { // if the content is formattable, the following code made it culture invariant, // for instance, the int, "30,000" can be formatted to "30-000" if the user // has a different language and region setting this.m_hashSourceBuilder.AppendLine(formatContent.ToString(null, CultureInfo.InvariantCulture)); } else { this.m_hashSourceBuilder.AppendLine(content.ToString()); } } else { this.m_hashSourceBuilder.AppendLine("NULL"); } } /// /// Add V2 schema properties and attributes to the hash builder /// /// /// private void AddV2ObjectContentToHashBuilder(object content, double version) { // if the version number is greater than or equal to V2, then we add the value if (version >= XmlConstants.EdmVersionForV2) { this.AddObjectContentToHashBuilder(content); } } internal static string GetMappingClosureHash(double mappingVersion, StorageEntityContainerMapping storageEntityContainerMapping) { Debug.Assert(storageEntityContainerMapping != null, "storageEntityContainerMapping is null!"); MetadataMappingHasherVisitor visitor = new MetadataMappingHasherVisitor(mappingVersion); visitor.Visit(storageEntityContainerMapping); return visitor.HashValue; } #endregion } }