Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Mapping / MetadataMappingHasherVisitor.cs
1 //---------------------------------------------------------------------
2 // <copyright file="MetadataMappingHasherVisitor.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5
6 // @owner       Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9
10 using System;
11 using System.Collections.Generic;
12 using System.Linq;
13 using System.Text;
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;
20
21
22 namespace System.Data.Mapping
23 {
24     internal partial class MetadataMappingHasherVisitor : BaseMetadataMappingVisitor
25     {
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;
32
33         private MetadataMappingHasherVisitor(double mappingVersion)
34         {
35             m_MappingVersion = mappingVersion;
36             this.m_hashSourceBuilder = new CompressingHashBuilder(MetadataHelper.CreateMetadataHashAlgorithm(m_MappingVersion));
37         }
38         
39         #region visitor method
40         protected override void Visit(StorageEntityContainerMapping storageEntityContainerMapping)
41         {
42             Debug.Assert(storageEntityContainerMapping != null, "storageEntityContainerMapping cannot be null!");
43
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;
48
49             this.m_EdmItemCollection = storageEntityContainerMapping.StorageMappingItemCollection.EdmItemCollection;
50
51             int index;
52             if (!this.AddObjectToSeenListAndHashBuilder(storageEntityContainerMapping, out index))
53             {
54                 // if this has been add to the seen list, then just 
55                 return;
56             }
57             if (this.m_itemsAlreadySeen.Count > 1)
58             {
59
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
62                 this.Clean();
63                 Visit(storageEntityContainerMapping);
64                 return;
65             }
66
67             this.AddObjectStartDumpToHashBuilder(storageEntityContainerMapping, index);
68
69             #region Inner data visit
70
71             this.AddObjectContentToHashBuilder(storageEntityContainerMapping.Identity);
72
73             this.AddV2ObjectContentToHashBuilder(storageEntityContainerMapping.GenerateUpdateViews, this.m_MappingVersion);
74
75             base.Visit(storageEntityContainerMapping);
76
77             #endregion
78
79             this.AddObjectEndDumpToHashBuilder();
80         }
81
82         protected override void Visit(EntityContainer entityContainer)
83         {
84             int index;
85             if (!this.AddObjectToSeenListAndHashBuilder(entityContainer, out index))
86             {
87                 return;
88             }
89
90             this.AddObjectStartDumpToHashBuilder(entityContainer, index);
91
92             #region Inner data visit
93             
94             this.AddObjectContentToHashBuilder(entityContainer.Identity);
95             // Name is covered by Identity
96
97             base.Visit(entityContainer);
98
99             #endregion
100
101             this.AddObjectEndDumpToHashBuilder();
102         }
103
104         protected override void Visit(StorageSetMapping storageSetMapping)
105         {
106             int index;
107             if (!this.AddObjectToSeenListAndHashBuilder(storageSetMapping, out index))
108             {
109                 return;
110             }
111
112             this.AddObjectStartDumpToHashBuilder(storageSetMapping, index);
113
114             #region Inner data visit
115             base.Visit(storageSetMapping);
116             #endregion
117
118             this.AddObjectEndDumpToHashBuilder();
119         }
120
121         protected override void Visit(StorageTypeMapping storageTypeMapping)
122         {
123             int index;
124             if (!this.AddObjectToSeenListAndHashBuilder(storageTypeMapping, out index))
125             {
126                 return;
127             }
128
129             this.AddObjectStartDumpToHashBuilder(storageTypeMapping, index);
130
131             #region Inner data visit
132
133             base.Visit(storageTypeMapping);
134
135             #endregion
136
137             this.AddObjectEndDumpToHashBuilder();
138         }
139
140         protected override void Visit(StorageMappingFragment storageMappingFragment)
141         {
142             int index;
143             if (!this.AddObjectToSeenListAndHashBuilder(storageMappingFragment, out index))
144             {
145                 return;
146             }
147
148             this.AddObjectStartDumpToHashBuilder(storageMappingFragment, index);
149
150             #region Inner data visit
151
152             this.AddV2ObjectContentToHashBuilder(storageMappingFragment.IsSQueryDistinct, this.m_MappingVersion);
153
154             base.Visit(storageMappingFragment);
155
156             #endregion
157
158             this.AddObjectEndDumpToHashBuilder();
159         }
160         
161         protected override void Visit(StoragePropertyMapping storagePropertyMapping)
162         {
163             base.Visit(storagePropertyMapping);
164         }
165
166         protected override void Visit(StorageComplexPropertyMapping storageComplexPropertyMapping)
167         {
168             int index;
169             if (!this.AddObjectToSeenListAndHashBuilder(storageComplexPropertyMapping, out index))
170             {
171                 return;
172             }
173
174             this.AddObjectStartDumpToHashBuilder(storageComplexPropertyMapping, index);
175
176             #region Inner data visit
177
178             base.Visit(storageComplexPropertyMapping);
179
180             #endregion
181
182             this.AddObjectEndDumpToHashBuilder();
183         }
184         protected override void Visit(StorageComplexTypeMapping storageComplexTypeMapping)
185         {
186             int index;
187             if (!this.AddObjectToSeenListAndHashBuilder(storageComplexTypeMapping, out index))
188             {
189                 return;
190             }
191
192             this.AddObjectStartDumpToHashBuilder(storageComplexTypeMapping, index);
193
194             #region Inner data visit
195
196             base.Visit(storageComplexTypeMapping);
197
198             #endregion
199
200             this.AddObjectEndDumpToHashBuilder();
201         }
202         
203         protected override void Visit(StorageConditionPropertyMapping storageConditionPropertyMapping)
204         {
205             int index;
206             if (!this.AddObjectToSeenListAndHashBuilder(storageConditionPropertyMapping, out index))
207             {
208                 return;
209             }
210
211             this.AddObjectStartDumpToHashBuilder(storageConditionPropertyMapping, index);
212
213             #region Inner data visit
214             this.AddObjectContentToHashBuilder(storageConditionPropertyMapping.IsNull);
215             this.AddObjectContentToHashBuilder(storageConditionPropertyMapping.Value);
216
217             base.Visit(storageConditionPropertyMapping);
218
219             #endregion
220
221             this.AddObjectEndDumpToHashBuilder();
222         }
223
224         protected override void Visit(StorageScalarPropertyMapping storageScalarPropertyMapping)
225         {
226             int index;
227             if (!this.AddObjectToSeenListAndHashBuilder(storageScalarPropertyMapping, out index))
228             {
229                 return;
230             }
231
232             this.AddObjectStartDumpToHashBuilder(storageScalarPropertyMapping, index);
233
234             #region Inner data visit
235
236             base.Visit(storageScalarPropertyMapping);
237
238             #endregion
239
240             this.AddObjectEndDumpToHashBuilder();
241         }
242         
243         protected override void Visit(EntitySetBase entitySetBase)
244         {
245             base.Visit(entitySetBase);
246         }
247         
248         protected override void Visit(EntitySet entitySet)
249         {
250             int index;
251             if (!this.AddObjectToSeenListAndHashBuilder(entitySet, out index))
252             {
253                 return;
254             }
255             #region Inner data visit
256
257             this.AddObjectStartDumpToHashBuilder(entitySet, index);
258             this.AddObjectContentToHashBuilder(entitySet.Name);
259             this.AddObjectContentToHashBuilder(entitySet.Schema);
260             this.AddObjectContentToHashBuilder(entitySet.Table);
261
262             base.Visit(entitySet);
263
264             foreach (var entityType in MetadataHelper.GetTypeAndSubtypesOf(entitySet.ElementType, this.m_EdmItemCollection, false).Where(type => type != entitySet.ElementType))
265             {
266                 this.Visit(entityType);
267             }
268
269             #endregion
270
271             this.AddObjectEndDumpToHashBuilder();
272         }
273
274         protected override void Visit(AssociationSet associationSet)
275         {
276             int index;
277             if (!this.AddObjectToSeenListAndHashBuilder(associationSet, out index))
278             {
279                 return;
280             }
281
282             this.AddObjectStartDumpToHashBuilder(associationSet, index);
283
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);
290
291             base.Visit(associationSet);
292
293             #endregion
294
295             this.AddObjectEndDumpToHashBuilder();
296         }
297
298         protected override void Visit(EntityType entityType)
299         {
300             int index;
301             if (!this.AddObjectToSeenListAndHashBuilder(entityType, out index))
302             {
303                 return;
304             }
305
306             this.AddObjectStartDumpToHashBuilder(entityType, index);
307
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
312
313             base.Visit(entityType);
314  
315             #endregion
316
317             this.AddObjectEndDumpToHashBuilder();
318         }
319
320         protected override void Visit(AssociationSetEnd associationSetEnd)
321         {
322             int index;
323             if (!this.AddObjectToSeenListAndHashBuilder(associationSetEnd, out index))
324             {
325                 return;
326             }
327
328             this.AddObjectStartDumpToHashBuilder(associationSetEnd, index);
329
330             #region Inner data visit
331             this.AddObjectContentToHashBuilder(associationSetEnd.Identity);
332             // Name is covered by Identity
333
334             base.Visit(associationSetEnd);
335
336             #endregion
337
338             this.AddObjectEndDumpToHashBuilder();
339         }
340
341         protected override void Visit(AssociationType associationType)
342         {
343             int index;
344             if (!this.AddObjectToSeenListAndHashBuilder(associationType, out index))
345             {
346                 return;
347             }
348
349             this.AddObjectStartDumpToHashBuilder(associationType, index);
350
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
355
356             base.Visit(associationType);
357
358             #endregion
359
360             this.AddObjectEndDumpToHashBuilder();
361         }
362
363         protected override void Visit(EdmProperty edmProperty)
364         {
365             int index;
366             if (!this.AddObjectToSeenListAndHashBuilder(edmProperty, out index))
367             {
368                 return;
369             }
370
371             this.AddObjectStartDumpToHashBuilder(edmProperty, index);
372
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);
383
384             base.Visit(edmProperty);
385
386             #endregion
387
388             this.AddObjectEndDumpToHashBuilder();
389         }
390
391         protected override void Visit(NavigationProperty navigationProperty)
392         {
393             // navigation properties are not considered in view generation
394             return;
395         }
396
397         protected override void Visit(EdmMember edmMember)
398         {
399             int index;
400             if (!this.AddObjectToSeenListAndHashBuilder(edmMember, out index))
401             {
402                 return;
403             }
404
405             this.AddObjectStartDumpToHashBuilder(edmMember, index);
406
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);
412
413             base.Visit(edmMember);
414
415             #endregion
416
417             this.AddObjectEndDumpToHashBuilder();
418         }
419
420         protected override void Visit(AssociationEndMember associationEndMember)
421         {
422             int index;
423             if (!this.AddObjectToSeenListAndHashBuilder(associationEndMember, out index))
424             {
425                 return;
426             }
427
428             this.AddObjectStartDumpToHashBuilder(associationEndMember, index);
429
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);
437
438             base.Visit(associationEndMember);
439
440             #endregion
441
442             this.AddObjectEndDumpToHashBuilder();
443         }
444         
445         protected override void Visit(ReferentialConstraint referentialConstraint)
446         {
447             int index;
448             if (!this.AddObjectToSeenListAndHashBuilder(referentialConstraint, out index))
449             {
450                 return;
451             }
452
453             this.AddObjectStartDumpToHashBuilder(referentialConstraint, index);
454
455             #region Inner data visit
456             this.AddObjectContentToHashBuilder(referentialConstraint.Identity);
457
458             base.Visit(referentialConstraint);
459
460             #endregion
461
462             this.AddObjectEndDumpToHashBuilder();
463         }
464
465         protected override void Visit(RelationshipEndMember relationshipEndMember)
466         {
467             int index;
468             if (!this.AddObjectToSeenListAndHashBuilder(relationshipEndMember, out index))
469             {
470                 return;
471             }
472
473             this.AddObjectStartDumpToHashBuilder(relationshipEndMember, index);
474
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);
482
483             base.Visit(relationshipEndMember);
484
485             #endregion
486
487             this.AddObjectEndDumpToHashBuilder();
488         }
489
490         protected override void Visit(TypeUsage typeUsage)
491         {
492             int index;
493             if (!this.AddObjectToSeenListAndHashBuilder(typeUsage, out index))
494             {
495                 return;
496             }
497
498             this.AddObjectStartDumpToHashBuilder(typeUsage, index);
499
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.
503
504             base.Visit(typeUsage);
505
506             #endregion
507
508             this.AddObjectEndDumpToHashBuilder();
509         }
510
511         protected override void Visit(RelationshipType relationshipType)
512         {
513             base.Visit(relationshipType);
514         }
515
516         protected override void Visit(EdmType edmType)
517         {
518             base.Visit(edmType);
519         }
520         
521         protected override void Visit(EnumType enumType)
522         {
523             int index;
524             if (!this.AddObjectToSeenListAndHashBuilder(enumType, out index))
525             {
526                 return;
527             }
528
529             this.AddObjectStartDumpToHashBuilder(enumType, index);
530
531             this.AddObjectContentToHashBuilder(enumType.Identity);
532             this.Visit(enumType.UnderlyingType);
533
534             base.Visit(enumType);
535
536             this.AddObjectEndDumpToHashBuilder();
537         }
538
539         protected override void Visit(EnumMember enumMember)
540         {
541             int index;
542             if (!this.AddObjectToSeenListAndHashBuilder(enumMember, out index))
543             {
544                 return;
545             }
546
547             this.AddObjectStartDumpToHashBuilder(enumMember, index);
548
549             this.AddObjectContentToHashBuilder(enumMember.Name);
550             this.AddObjectContentToHashBuilder(enumMember.Value);
551
552             base.Visit(enumMember);
553
554             this.AddObjectEndDumpToHashBuilder();
555         }
556
557         protected override void Visit(CollectionType collectionType)
558         {
559             int index;
560             if (!this.AddObjectToSeenListAndHashBuilder(collectionType, out index))
561             {
562                 return;
563             }
564
565             this.AddObjectStartDumpToHashBuilder(collectionType, index);
566
567             #region Inner data visit
568             this.AddObjectContentToHashBuilder(collectionType.Identity);
569             // Identity contains Name, NamespaceName and FullName
570
571             base.Visit(collectionType);
572
573             #endregion
574
575             this.AddObjectEndDumpToHashBuilder();
576         }
577         
578         protected override void Visit(RefType refType)
579         {
580             int index;
581             if (!this.AddObjectToSeenListAndHashBuilder(refType, out index))
582             {
583                 return;
584             }
585
586             this.AddObjectStartDumpToHashBuilder(refType, index);
587
588             #region Inner data visit
589             this.AddObjectContentToHashBuilder(refType.Identity);
590             // Identity contains Name, NamespaceName and FullName
591
592             base.Visit(refType);
593
594             #endregion
595
596             this.AddObjectEndDumpToHashBuilder();
597         }
598
599         protected override void Visit(EntityTypeBase entityTypeBase)
600         {
601             base.Visit(entityTypeBase);
602         }
603
604         protected override void Visit(Facet facet)
605         {
606             int index;
607             if (facet.Name != DbProviderManifest.NullableFacetName)
608             {
609                 // skip all the non interesting facets
610                 return;
611             }
612
613             if (!this.AddObjectToSeenListAndHashBuilder(facet, out index))
614             {
615                 return;
616             }
617
618             this.AddObjectStartDumpToHashBuilder(facet, index);
619
620             #region Inner data visit
621             this.AddObjectContentToHashBuilder(facet.Identity);
622             // Identity already contains Name
623             this.AddObjectContentToHashBuilder(facet.Value);
624
625             base.Visit(facet);
626
627             #endregion
628
629             this.AddObjectEndDumpToHashBuilder();
630         }
631
632         protected override void Visit(EdmFunction edmFunction)
633         {
634             // View Generation doesn't deal with functions
635             // so just return;
636         }
637         
638         protected override void Visit(ComplexType complexType)
639         {
640             int index;
641             if (!this.AddObjectToSeenListAndHashBuilder(complexType, out index))
642             {
643                 return;
644             }
645
646             this.AddObjectStartDumpToHashBuilder(complexType, index);
647
648             #region Inner data visit
649             this.AddObjectContentToHashBuilder(complexType.Abstract);
650             this.AddObjectContentToHashBuilder(complexType.Identity);
651             // Identity covers, FullName, Name, and NamespaceName
652
653             base.Visit(complexType);
654
655             #endregion
656
657             this.AddObjectEndDumpToHashBuilder();
658         }
659         
660         protected override void Visit(PrimitiveType primitiveType)
661         {
662             int index;
663             if (!this.AddObjectToSeenListAndHashBuilder(primitiveType, out index))
664             {
665                 return;
666             }
667
668             this.AddObjectStartDumpToHashBuilder(primitiveType, index);
669
670             #region Inner data visit
671             this.AddObjectContentToHashBuilder(primitiveType.Name);
672             this.AddObjectContentToHashBuilder(primitiveType.NamespaceName);
673
674             base.Visit(primitiveType);
675
676             #endregion
677
678             this.AddObjectEndDumpToHashBuilder();
679         }
680         
681         protected override void Visit(FunctionParameter functionParameter)
682         {
683             int index;
684             if (!this.AddObjectToSeenListAndHashBuilder(functionParameter, out index))
685             {
686                 return;
687             }
688
689             this.AddObjectStartDumpToHashBuilder(functionParameter, index);
690
691             #region Inner data visit
692             this.AddObjectContentToHashBuilder(functionParameter.Identity);
693             // Identity already has Name
694             this.AddObjectContentToHashBuilder(functionParameter.Mode);
695
696             base.Visit(functionParameter);
697
698             #endregion
699
700             this.AddObjectEndDumpToHashBuilder();
701         }
702
703         protected override void Visit(DbProviderManifest providerManifest)
704         {
705             // the provider manifest will be checked by all the other types lining up.
706             // no need to store more info.
707         }
708         #endregion
709
710         #region hasher helper method
711
712         internal string HashValue
713         {
714             get
715             {
716                 return m_hashSourceBuilder.ComputeHash();
717             }
718         }
719
720         private void Clean()
721         {
722             this.m_hashSourceBuilder = new CompressingHashBuilder(MetadataHelper.CreateMetadataHashAlgorithm(m_MappingVersion));
723             this.m_instanceNumber = 0;
724             this.m_itemsAlreadySeen = new Dictionary<object, int>();
725         }
726
727         /// <summary>
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
730         /// </summary>
731         /// <param name="o"></param>
732         /// <param name="indexSeen"></param>
733         /// <returns></returns>
734         private bool TryAddSeenItem(Object o, out int indexSeen)
735         {
736             if (!this.m_itemsAlreadySeen.TryGetValue(o, out indexSeen))
737             {
738                 this.m_itemsAlreadySeen.Add(o, this.m_instanceNumber);
739
740                 indexSeen = this.m_instanceNumber;
741                 this.m_instanceNumber++;
742
743                 return true;
744             }
745             return false;
746         }
747
748         /// <summary>
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
751         /// </summary>
752         /// <param name="o"></param>
753         /// <returns></returns>
754         private bool AddObjectToSeenListAndHashBuilder(object o, out int instanceIndex)
755         {
756             if (o == null)
757             {
758                 instanceIndex = -1;
759                 return false;
760             }
761             if (!TryAddSeenItem(o, out instanceIndex))
762             {
763                 this.AddObjectStartDumpToHashBuilder(o, instanceIndex);
764                 this.AddSeenObjectToHashBuilder(o, instanceIndex);
765                 this.AddObjectEndDumpToHashBuilder();
766                 return false;
767             }
768             return true;
769         }
770
771         private void AddSeenObjectToHashBuilder(object o, int instanceIndex)
772         {
773             Debug.Assert(instanceIndex >= 0, "referencing index should not be less than 0");
774             this.m_hashSourceBuilder.AppendLine("Instance Reference: " + instanceIndex);
775         }
776
777         private void AddObjectStartDumpToHashBuilder(object o, int objectIndex)
778         {
779             this.m_hashSourceBuilder.AppendObjectStartDump(o, objectIndex);
780         }
781
782         private void AddObjectEndDumpToHashBuilder()
783         {
784             this.m_hashSourceBuilder.AppendObjectEndDump();
785         }
786
787         private void AddObjectContentToHashBuilder(object content)
788         {
789             if (content != null)
790             {
791                 IFormattable formatContent = content as IFormattable;
792                 if (formatContent != null)
793                 {
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));
798                 }
799                 else
800                 {
801                     this.m_hashSourceBuilder.AppendLine(content.ToString());
802                 }
803             }
804             else
805             {
806                 this.m_hashSourceBuilder.AppendLine("NULL");
807             }
808         }
809
810         /// <summary>
811         /// Add V2 schema properties and attributes to the hash builder
812         /// </summary>
813         /// <param name="content"></param>
814         /// <param name="defaultValue"></param>
815         private void AddV2ObjectContentToHashBuilder(object content, double version)
816         {
817             // if the version number is greater than or equal to V2, then we add the value
818             if (version >= XmlConstants.EdmVersionForV2)
819             {
820                 this.AddObjectContentToHashBuilder(content);
821             }
822         }
823
824         internal static string GetMappingClosureHash(double mappingVersion, StorageEntityContainerMapping storageEntityContainerMapping)
825         {
826             Debug.Assert(storageEntityContainerMapping != null, "storageEntityContainerMapping is null!");
827
828             MetadataMappingHasherVisitor visitor = new MetadataMappingHasherVisitor(mappingVersion);
829             visitor.Visit(storageEntityContainerMapping);
830             return visitor.HashValue;
831         }
832         #endregion
833
834     }
835 }