Skip to content

Commit 2cc6a8d

Browse files
committed
Trying to fix class map
1 parent 856cfb8 commit 2cc6a8d

File tree

4 files changed

+439
-204
lines changed

4 files changed

+439
-204
lines changed

src/MongoDB.Bson/Serialization/BsonClassMap.cs

Lines changed: 43 additions & 194 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,6 @@ namespace MongoDB.Bson.Serialization
3131
/// </summary>
3232
public class BsonClassMap
3333
{
34-
// private static fields
35-
private readonly static Dictionary<Type, BsonClassMap> __classMaps = new Dictionary<Type, BsonClassMap>();
36-
private readonly static Queue<Type> __knownTypesQueue = new Queue<Type>();
37-
private static int __freezeNestingLevel = 0;
38-
3934
// private fields
4035
private readonly Type _classType;
4136
private readonly List<BsonCreatorMap> _creatorMaps;
@@ -282,103 +277,32 @@ public static Type GetMemberInfoType(MemberInfo memberInfo)
282277
/// Gets all registered class maps.
283278
/// </summary>
284279
/// <returns>All registered class maps.</returns>
285-
public static IEnumerable<BsonClassMap> GetRegisteredClassMaps()
286-
{
287-
BsonSerializer.ConfigLock.EnterReadLock(); //TODO It would make sense to look at this after the PR by Robert is merged
288-
try
289-
{
290-
return __classMaps.Values.ToList(); // return a copy for thread safety
291-
}
292-
finally
293-
{
294-
BsonSerializer.ConfigLock.ExitReadLock();
295-
}
296-
}
280+
public static IEnumerable<BsonClassMap> GetRegisteredClassMaps() =>
281+
BsonSerializer.DefaultDomain.ClassMapDomain.GetRegisteredClassMaps();
297282

298283
/// <summary>
299284
/// Checks whether a class map is registered for a type.
300285
/// </summary>
301286
/// <param name="type">The type to check.</param>
302287
/// <returns>True if there is a class map registered for the type.</returns>
303-
public static bool IsClassMapRegistered(Type type)
304-
{
305-
if (type == null)
306-
{
307-
throw new ArgumentNullException("type");
308-
}
309-
310-
BsonSerializer.ConfigLock.EnterReadLock();
311-
try
312-
{
313-
return __classMaps.ContainsKey(type);
314-
}
315-
finally
316-
{
317-
BsonSerializer.ConfigLock.ExitReadLock();
318-
}
319-
}
288+
public static bool IsClassMapRegistered(Type type) =>
289+
BsonSerializer.DefaultDomain.ClassMapDomain.IsClassMapRegistered(type);
320290

321291
/// <summary>
322292
/// Looks up a class map (will AutoMap the class if no class map is registered).
323293
/// </summary>
324294
/// <param name="classType">The class type.</param>
325295
/// <returns>The class map.</returns>
326-
public static BsonClassMap LookupClassMap(Type classType)
327-
{
328-
if (classType == null)
329-
{
330-
throw new ArgumentNullException("classType");
331-
}
332-
333-
BsonSerializer.ConfigLock.EnterReadLock();
334-
try
335-
{
336-
if (__classMaps.TryGetValue(classType, out var classMap))
337-
{
338-
if (classMap.IsFrozen)
339-
{
340-
return classMap;
341-
}
342-
}
343-
}
344-
finally
345-
{
346-
BsonSerializer.ConfigLock.ExitReadLock();
347-
}
348-
349-
// automatically create a new classMap for classType and register it (unless another thread does first)
350-
// do the work of speculatively creating a new class map outside of holding any lock
351-
var classMapDefinition = typeof(BsonClassMap<>);
352-
var classMapType = classMapDefinition.MakeGenericType(classType);
353-
var newClassMap = (BsonClassMap)Activator.CreateInstance(classMapType);
354-
newClassMap.AutoMap();
355-
356-
BsonSerializer.ConfigLock.EnterWriteLock();
357-
try
358-
{
359-
if (!__classMaps.TryGetValue(classType, out var classMap))
360-
{
361-
RegisterClassMap(newClassMap);
362-
classMap = newClassMap;
363-
}
364-
365-
return classMap.Freeze();
366-
}
367-
finally
368-
{
369-
BsonSerializer.ConfigLock.ExitWriteLock();
370-
}
371-
}
296+
public static BsonClassMap LookupClassMap(Type classType) =>
297+
BsonSerializer.DefaultDomain.ClassMapDomain.LookupClassMap(classType);
372298

373299
/// <summary>
374300
/// Creates and registers a class map.
375301
/// </summary>
376302
/// <typeparam name="TClass">The class.</typeparam>
377303
/// <returns>The class map.</returns>
378-
public static BsonClassMap<TClass> RegisterClassMap<TClass>() //TODO We should move the static methods here to IBSonSerializerDomain
379-
{
380-
return RegisterClassMap<TClass>(cm => { cm.AutoMap(); });
381-
}
304+
public static BsonClassMap<TClass> RegisterClassMap<TClass>()=>
305+
BsonSerializer.DefaultDomain.ClassMapDomain.RegisterClassMap<TClass>();
382306

383307
/// <summary>
384308
/// Creates and registers a class map.
@@ -387,52 +311,22 @@ public static BsonClassMap<TClass> RegisterClassMap<TClass>() //TODO We should
387311
/// <param name="classMapInitializer">The class map initializer.</param>
388312
/// <returns>The class map.</returns>
389313
public static BsonClassMap<TClass> RegisterClassMap<TClass>(Action<BsonClassMap<TClass>> classMapInitializer)
390-
{
391-
var classMap = new BsonClassMap<TClass>(classMapInitializer);
392-
RegisterClassMap(classMap);
393-
return classMap;
394-
}
314+
=> BsonSerializer.DefaultDomain.ClassMapDomain.RegisterClassMap(classMapInitializer);
395315

396316
/// <summary>
397317
/// Registers a class map.
398318
/// </summary>
399319
/// <param name="classMap">The class map.</param>
400320
public static void RegisterClassMap(BsonClassMap classMap)
401-
{
402-
if (classMap == null)
403-
{
404-
throw new ArgumentNullException("classMap");
405-
}
406-
407-
BsonSerializer.ConfigLock.EnterWriteLock();
408-
try
409-
{
410-
// note: class maps can NOT be replaced (because derived classes refer to existing instance)
411-
__classMaps.Add(classMap.ClassType, classMap);
412-
BsonSerializer.RegisterDiscriminator(classMap.ClassType, classMap.Discriminator);
413-
}
414-
finally
415-
{
416-
BsonSerializer.ConfigLock.ExitWriteLock();
417-
}
418-
}
321+
=> BsonSerializer.DefaultDomain.ClassMapDomain.RegisterClassMap(classMap);
419322

420323
/// <summary>
421324
/// Registers a class map if it is not already registered.
422325
/// </summary>
423326
/// <typeparam name="TClass">The class.</typeparam>
424327
/// <returns>True if this call registered the class map, false if the class map was already registered.</returns>
425328
public static bool TryRegisterClassMap<TClass>()
426-
{
427-
return TryRegisterClassMap(ClassMapFactory);
428-
429-
static BsonClassMap<TClass> ClassMapFactory()
430-
{
431-
var classMap = new BsonClassMap<TClass>();
432-
classMap.AutoMap();
433-
return classMap;
434-
}
435-
}
329+
=> BsonSerializer.DefaultDomain.ClassMapDomain.TryRegisterClassMap<TClass>();
436330

437331
/// <summary>
438332
/// Registers a class map if it is not already registered.
@@ -441,19 +335,7 @@ static BsonClassMap<TClass> ClassMapFactory()
441335
/// <param name="classMap">The class map.</param>
442336
/// <returns>True if this call registered the class map, false if the class map was already registered.</returns>
443337
public static bool TryRegisterClassMap<TClass>(BsonClassMap<TClass> classMap)
444-
{
445-
if (classMap == null)
446-
{
447-
throw new ArgumentNullException(nameof(classMap));
448-
}
449-
450-
return TryRegisterClassMap(ClassMapFactory);
451-
452-
BsonClassMap<TClass> ClassMapFactory()
453-
{
454-
return classMap;
455-
}
456-
}
338+
=> BsonSerializer.DefaultDomain.ClassMapDomain.TryRegisterClassMap(classMap);
457339

458340
/// <summary>
459341
/// Registers a class map if it is not already registered.
@@ -462,19 +344,7 @@ BsonClassMap<TClass> ClassMapFactory()
462344
/// <param name="classMapInitializer">The class map initializer (only called if the class map is not already registered).</param>
463345
/// <returns>True if this call registered the class map, false if the class map was already registered.</returns>
464346
public static bool TryRegisterClassMap<TClass>(Action<BsonClassMap<TClass>> classMapInitializer)
465-
{
466-
if (classMapInitializer == null)
467-
{
468-
throw new ArgumentNullException(nameof(classMapInitializer));
469-
}
470-
471-
return TryRegisterClassMap(ClassMapFactory);
472-
473-
BsonClassMap<TClass> ClassMapFactory()
474-
{
475-
return new BsonClassMap<TClass>(classMapInitializer);
476-
}
477-
}
347+
=> BsonSerializer.DefaultDomain.ClassMapDomain.TryRegisterClassMap(classMapInitializer);
478348

479349
/// <summary>
480350
/// Registers a class map if it is not already registered.
@@ -483,45 +353,7 @@ BsonClassMap<TClass> ClassMapFactory()
483353
/// <param name="classMapFactory">The class map factory (only called if the class map is not already registered).</param>
484354
/// <returns>True if this call registered the class map, false if the class map was already registered.</returns>
485355
public static bool TryRegisterClassMap<TClass>(Func<BsonClassMap<TClass>> classMapFactory)
486-
{
487-
if (classMapFactory == null)
488-
{
489-
throw new ArgumentNullException(nameof(classMapFactory));
490-
}
491-
492-
BsonSerializer.ConfigLock.EnterReadLock();
493-
try
494-
{
495-
if (__classMaps.ContainsKey(typeof(TClass)))
496-
{
497-
return false;
498-
}
499-
}
500-
finally
501-
{
502-
BsonSerializer.ConfigLock.ExitReadLock();
503-
}
504-
505-
BsonSerializer.ConfigLock.EnterWriteLock();
506-
try
507-
{
508-
if (__classMaps.ContainsKey(typeof(TClass)))
509-
{
510-
return false;
511-
}
512-
else
513-
{
514-
// create a classMap for TClass and register it
515-
var classMap = classMapFactory();
516-
RegisterClassMap(classMap);
517-
return true;
518-
}
519-
}
520-
finally
521-
{
522-
BsonSerializer.ConfigLock.ExitWriteLock();
523-
}
524-
}
356+
=> BsonSerializer.DefaultDomain.ClassMapDomain.TryRegisterClassMap(classMapFactory);
525357

526358
// public methods
527359
/// <summary>
@@ -573,13 +405,30 @@ obj is BsonClassMap other &&
573405
/// <inheritdoc/>
574406
public override int GetHashCode() => 0;
575407

408+
internal class FreezeContext
409+
{
410+
public int FreezeNestingLevel { get; set; } = 0;
411+
public Queue<Type> KnownTypesQueue { get; set; } = new();
412+
public IBsonSerializationDomain SerializationDomain { get; set; }
413+
}
414+
576415
/// <summary>
577416
/// Freezes the class map.
578417
/// </summary>
579418
/// <returns>The frozen class map.</returns>
580-
public BsonClassMap Freeze()
419+
public BsonClassMap Freeze() => Freeze(BsonSerializer.DefaultDomain);
420+
421+
422+
internal BsonClassMap Freeze(IBsonSerializationDomain domain)
423+
{
424+
var freezeContext = new FreezeContext() { SerializationDomain = domain };
425+
return Freeze(freezeContext);
426+
}
427+
428+
private BsonClassMap Freeze(FreezeContext context)
581429
{
582-
BsonSerializer.ConfigLock.EnterReadLock();
430+
var configLock = (context.SerializationDomain as IBsonSerializationDomainInternal)!.ConfigLock;
431+
configLock.EnterReadLock();
583432
try
584433
{
585434
if (_frozen)
@@ -589,15 +438,15 @@ public BsonClassMap Freeze()
589438
}
590439
finally
591440
{
592-
BsonSerializer.ConfigLock.ExitReadLock();
441+
configLock.ExitReadLock();
593442
}
594443

595-
BsonSerializer.ConfigLock.EnterWriteLock();
444+
configLock.EnterWriteLock();
596445
try
597446
{
598447
if (!_frozen)
599448
{
600-
__freezeNestingLevel++;
449+
context.FreezeNestingLevel++;
601450
try
602451
{
603452
var baseType = _classType.GetTypeInfo().BaseType;
@@ -607,7 +456,7 @@ public BsonClassMap Freeze()
607456
{
608457
_baseClassMap = LookupClassMap(baseType);
609458
}
610-
_baseClassMap.Freeze();
459+
_baseClassMap.Freeze(context); //TODO This is not necessary, because LookupClassMap will only return a frozen class map
611460
_discriminatorIsRequired |= _baseClassMap._discriminatorIsRequired;
612461
_hasRootClass |= (_isRootClass || _baseClassMap.HasRootClass);
613462
_allMemberMaps.AddRange(_baseClassMap.AllMemberMaps);
@@ -699,28 +548,28 @@ public BsonClassMap Freeze()
699548
// this avoids infinite recursion when going back down the inheritance tree while processing known types
700549
foreach (var knownType in _knownTypes)
701550
{
702-
__knownTypesQueue.Enqueue(knownType);
551+
context.KnownTypesQueue.Enqueue(knownType);
703552
}
704553

705554
// if we are back to the first level go ahead and process any queued known types
706-
if (__freezeNestingLevel == 1)
555+
if (context.FreezeNestingLevel == 1)
707556
{
708-
while (__knownTypesQueue.Count != 0)
557+
while (context.KnownTypesQueue.Count != 0)
709558
{
710-
var knownType = __knownTypesQueue.Dequeue();
559+
var knownType = context.KnownTypesQueue.Dequeue();
711560
LookupClassMap(knownType); // will AutoMap and/or Freeze knownType if necessary
712561
}
713562
}
714563
}
715564
finally
716565
{
717-
__freezeNestingLevel--;
566+
context.FreezeNestingLevel--;
718567
}
719568
}
720569
}
721570
finally
722571
{
723-
BsonSerializer.ConfigLock.ExitWriteLock();
572+
configLock.ExitWriteLock();
724573
}
725574
return this;
726575
}

0 commit comments

Comments
 (0)