14
14
*/
15
15
16
16
using System ;
17
+ using System . Collections ;
17
18
using System . Collections . Generic ;
18
19
using System . IO ;
19
- using System . Linq ;
20
20
using System . Reflection ;
21
21
using System . Threading ;
22
22
25
25
using MongoDB . Bson . Serialization . Attributes ;
26
26
using MongoDB . Bson . Serialization . Conventions ;
27
27
using MongoDB . Bson . Serialization . IdGenerators ;
28
- using MongoDB . Bson . Serialization . Serializers ;
29
28
30
29
namespace MongoDB . Bson . Serialization
31
30
{
@@ -38,11 +37,11 @@ public static class BsonSerializer
38
37
private static ReaderWriterLockSlim __configLock = new ReaderWriterLockSlim ( LockRecursionPolicy . SupportsRecursion ) ;
39
38
private static Dictionary < Type , IIdGenerator > __idGenerators = new Dictionary < Type , IIdGenerator > ( ) ;
40
39
private static Dictionary < Type , IDiscriminatorConvention > __discriminatorConventions = new Dictionary < Type , IDiscriminatorConvention > ( ) ;
41
- private static Dictionary < BsonValue , HashSet < Type > > __discriminators = new Dictionary < BsonValue , HashSet < Type > > ( ) ;
42
- private static HashSet < Type > __discriminatedTypes = new HashSet < Type > ( ) ;
40
+ private static Hashtable __discriminators = new Hashtable ( EqualityComparer < BsonValue > . Default ) ; // effectively a Dictionary<BsonValue, HashSet<Type>>
41
+ private static Hashtable __discriminatedTypes = new Hashtable ( ) ;
43
42
private static BsonSerializerRegistry __serializerRegistry ;
44
43
private static TypeMappingSerializationProvider __typeMappingSerializationProvider ;
45
- private static HashSet < Type > __typesWithRegisteredKnownTypes = new HashSet < Type > ( ) ;
44
+ private static Hashtable __typesWithRegisteredKnownTypes = new Hashtable ( EqualityComparer < Type > . Default ) ;
46
45
47
46
private static bool __useNullIdChecker = false ;
48
47
private static bool __useZeroIdChecker = false ;
@@ -276,7 +275,7 @@ public static object Deserialize(TextReader textReader, Type nominalType, Action
276
275
public static bool IsTypeDiscriminated ( Type type )
277
276
{
278
277
var typeInfo = type . GetTypeInfo ( ) ;
279
- return typeInfo . IsInterface || __discriminatedTypes . Contains ( type ) ;
278
+ return typeInfo . IsInterface || __discriminatedTypes . ContainsKey ( type ) ;
280
279
}
281
280
282
281
/// <summary>
@@ -295,56 +294,49 @@ public static Type LookupActualType(Type nominalType, BsonValue discriminator)
295
294
// note: EnsureKnownTypesAreRegistered handles its own locking so call from outside any lock
296
295
EnsureKnownTypesAreRegistered ( nominalType ) ;
297
296
298
- __configLock . EnterReadLock ( ) ;
299
- try
300
- {
301
- Type actualType = null ;
297
+ Type actualType = null ;
302
298
303
- HashSet < Type > hashSet ;
304
- if ( __discriminators . TryGetValue ( discriminator , out hashSet ) )
299
+ object hashSetAsObject = __discriminators [ discriminator ] ;
300
+ if ( hashSetAsObject != null )
301
+ {
302
+ HashSet < Type > hashSet = ( HashSet < Type > ) __discriminators [ discriminator ] ;
303
+ foreach ( var type in hashSet )
305
304
{
306
- foreach ( var type in hashSet )
305
+ if ( nominalType . GetTypeInfo ( ) . IsAssignableFrom ( type ) )
307
306
{
308
- if ( nominalType . GetTypeInfo ( ) . IsAssignableFrom ( type ) )
307
+ if ( actualType == null )
309
308
{
310
- if ( actualType == null )
311
- {
312
- actualType = type ;
313
- }
314
- else
315
- {
316
- string message = string . Format ( "Ambiguous discriminator '{0}'." , discriminator ) ;
317
- throw new BsonSerializationException ( message ) ;
318
- }
309
+ actualType = type ;
310
+ }
311
+ else
312
+ {
313
+ string message = string . Format ( "Ambiguous discriminator '{0}'." , discriminator ) ;
314
+ throw new BsonSerializationException ( message ) ;
319
315
}
320
316
}
321
317
}
318
+ }
322
319
323
- if ( actualType == null && discriminator . IsString )
324
- {
325
- actualType = TypeNameDiscriminator . GetActualType ( discriminator . AsString ) ; // see if it's a Type name
326
- }
327
-
328
- if ( actualType == null )
329
- {
330
- string message = string . Format ( "Unknown discriminator value '{0}'." , discriminator ) ;
331
- throw new BsonSerializationException ( message ) ;
332
- }
333
-
334
- if ( ! nominalType . GetTypeInfo ( ) . IsAssignableFrom ( actualType ) )
335
- {
336
- string message = string . Format (
337
- "Actual type {0} is not assignable to expected type {1}." ,
338
- actualType . FullName , nominalType . FullName ) ;
339
- throw new BsonSerializationException ( message ) ;
340
- }
320
+ if ( actualType == null && discriminator . IsString )
321
+ {
322
+ actualType = TypeNameDiscriminator . GetActualType ( discriminator . AsString ) ; // see if it's a Type name
323
+ }
341
324
342
- return actualType ;
325
+ if ( actualType == null )
326
+ {
327
+ string message = string . Format ( "Unknown discriminator value '{0}'." , discriminator ) ;
328
+ throw new BsonSerializationException ( message ) ;
343
329
}
344
- finally
330
+
331
+ if ( ! nominalType . GetTypeInfo ( ) . IsAssignableFrom ( actualType ) )
345
332
{
346
- __configLock . ExitReadLock ( ) ;
333
+ string message = string . Format (
334
+ "Actual type {0} is not assignable to expected type {1}." ,
335
+ actualType . FullName , nominalType . FullName ) ;
336
+ throw new BsonSerializationException ( message ) ;
347
337
}
338
+
339
+ return actualType ;
348
340
}
349
341
350
342
/// <summary>
@@ -504,6 +496,17 @@ public static IBsonSerializer LookupSerializer(Type type)
504
496
return __serializerRegistry . GetSerializer ( type ) ;
505
497
}
506
498
499
+ private static void PopulateHashSet ( HashSet < Type > hashSet , Type type )
500
+ {
501
+ hashSet . Add ( type ) ;
502
+
503
+ // mark all base types as discriminated (so we know that it's worth reading a discriminator)
504
+ for ( var baseType = type . GetTypeInfo ( ) . BaseType ; baseType != null ; baseType = baseType . GetTypeInfo ( ) . BaseType )
505
+ {
506
+ __discriminatedTypes [ baseType ] = null /* we don't care about the value */ ;
507
+ }
508
+ }
509
+
507
510
/// <summary>
508
511
/// Registers the discriminator for a type.
509
512
/// </summary>
@@ -519,23 +522,27 @@ public static void RegisterDiscriminator(Type type, BsonValue discriminator)
519
522
}
520
523
521
524
__configLock . EnterWriteLock ( ) ;
525
+
522
526
try
523
527
{
524
- HashSet < Type > hashSet ;
525
- if ( ! __discriminators . TryGetValue ( discriminator , out hashSet ) )
528
+ object hashSetAsObject = __discriminators [ discriminator ] ;
529
+ if ( hashSetAsObject == null )
526
530
{
527
- hashSet = new HashSet < Type > ( ) ;
528
- __discriminators . Add ( discriminator , hashSet ) ;
531
+ // straight forward: discriminator was unknown so we just add a new hash set
532
+ HashSet < Type > hashSet = new HashSet < Type > ( ) ;
533
+ PopulateHashSet ( hashSet , type ) ;
534
+ __discriminators [ discriminator ] = hashSet ;
529
535
}
530
-
531
- if ( ! hashSet . Contains ( type ) )
536
+ else
532
537
{
533
- hashSet . Add ( type ) ;
534
-
535
- // mark all base types as discriminated (so we know that it's worth reading a discriminator)
536
- for ( var baseType = type . GetTypeInfo ( ) . BaseType ; baseType != null ; baseType = baseType . GetTypeInfo ( ) . BaseType )
538
+ HashSet < Type > hashSet = ( HashSet < Type > ) hashSetAsObject ;
539
+ if ( ! hashSet . Contains ( type ) )
537
540
{
538
- __discriminatedTypes . Add ( baseType ) ;
541
+ // an existing hash set was there so we duplicate it and replace the old one in the cache with a new instance
542
+ // so that consumers can safely iterate over it because we don't modify the same instance
543
+ hashSet = new HashSet < Type > ( hashSet ) ;
544
+ PopulateHashSet ( hashSet , type ) ;
545
+ __discriminators [ discriminator ] = hashSet ;
539
546
}
540
547
}
541
548
}
@@ -672,23 +679,15 @@ public static void Serialize(
672
679
// internal static methods
673
680
internal static void EnsureKnownTypesAreRegistered ( Type nominalType )
674
681
{
675
- __configLock . EnterReadLock ( ) ;
676
- try
682
+ if ( __typesWithRegisteredKnownTypes . ContainsKey ( nominalType ) )
677
683
{
678
- if ( __typesWithRegisteredKnownTypes . Contains ( nominalType ) )
679
- {
680
- return ;
681
- }
682
- }
683
- finally
684
- {
685
- __configLock . ExitReadLock ( ) ;
684
+ return ;
686
685
}
687
686
688
687
__configLock . EnterWriteLock ( ) ;
689
688
try
690
689
{
691
- if ( ! __typesWithRegisteredKnownTypes . Contains ( nominalType ) )
690
+ if ( ! __typesWithRegisteredKnownTypes . ContainsKey ( nominalType ) )
692
691
{
693
692
// only call LookupClassMap for classes with a BsonKnownTypesAttribute
694
693
#if NET452
@@ -702,7 +701,7 @@ internal static void EnsureKnownTypesAreRegistered(Type nominalType)
702
701
LookupSerializer ( nominalType ) ;
703
702
}
704
703
705
- __typesWithRegisteredKnownTypes . Add ( nominalType ) ;
704
+ __typesWithRegisteredKnownTypes [ nominalType ] = null /* we don't care about the value */ ;
706
705
}
707
706
}
708
707
finally
0 commit comments