2016-08-03 10:59:49 +00:00
//---------------------------------------------------------------------
// <copyright file="MetadataMappingHasherVisitor.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
2017-08-21 15:34:15 +00:00
// @owner Microsoft
// @backupOwner Microsoft
2016-08-03 10:59:49 +00:00
//---------------------------------------------------------------------
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 < Object , int > m_itemsAlreadySeen = new Dictionary < Object , int > ( ) ;
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 < object , int > ( ) ;
}
/// <summary>
/// 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
/// </summary>
/// <param name="o"></param>
/// <param name="indexSeen"></param>
/// <returns></returns>
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 ;
}
/// <summary>
/// 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
/// </summary>
/// <param name="o"></param>
/// <returns></returns>
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" ) ;
}
}
/// <summary>
/// Add V2 schema properties and attributes to the hash builder
/// </summary>
/// <param name="content"></param>
/// <param name="defaultValue"></param>
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
}
}