diff --git a/src/MongoDB.Bson/BsonDefaults.cs b/src/MongoDB.Bson/BsonDefaults.cs
index 9352d3228ff..61e97ac8b66 100644
--- a/src/MongoDB.Bson/BsonDefaults.cs
+++ b/src/MongoDB.Bson/BsonDefaults.cs
@@ -24,33 +24,14 @@ namespace MongoDB.Bson
///
public static class BsonDefaults
{
- // private static fields
- private static bool __dynamicArraySerializerWasSet;
- private static IBsonSerializer __dynamicArraySerializer;
- private static bool __dynamicDocumentSerializerWasSet;
- private static IBsonSerializer __dynamicDocumentSerializer;
- private static int __maxDocumentSize = int.MaxValue;
- private static int __maxSerializationDepth = 100;
-
// public static properties
///
/// Gets or sets the dynamic array serializer.
///
public static IBsonSerializer DynamicArraySerializer
{
- get
- {
- if (!__dynamicArraySerializerWasSet)
- {
- __dynamicArraySerializer = BsonSerializer.LookupSerializer>();
- }
- return __dynamicArraySerializer;
- }
- set
- {
- __dynamicArraySerializerWasSet = true;
- __dynamicArraySerializer = value;
- }
+ get => BsonSerializationDomain.Default.BsonDefaults.DynamicArraySerializer;
+ set => BsonSerializationDomain.Default.BsonDefaults.DynamicArraySerializer = value;
}
///
@@ -58,28 +39,22 @@ public static IBsonSerializer DynamicArraySerializer
///
public static IBsonSerializer DynamicDocumentSerializer
{
- get
- {
- if (!__dynamicDocumentSerializerWasSet)
- {
- __dynamicDocumentSerializer = BsonSerializer.LookupSerializer();
- }
- return __dynamicDocumentSerializer;
- }
- set
- {
- __dynamicDocumentSerializerWasSet = true;
- __dynamicDocumentSerializer = value;
- }
+ get => BsonSerializationDomain.Default.BsonDefaults.DynamicDocumentSerializer;
+ set => BsonSerializationDomain.Default.BsonDefaults.DynamicDocumentSerializer = value;
}
+ /* DOMAIN-API We should modify the API to have those two values (and in the writer/reader settings where they are used) be nullable.
+ * The problem is that we need to now when these values have been set externally or not. If they have not, then they should
+ * be retrieved from the closest domain.
+ */
+
///
/// Gets or sets the default max document size. The default is 4MiB.
///
public static int MaxDocumentSize
{
- get { return __maxDocumentSize; }
- set { __maxDocumentSize = value; }
+ get => BsonSerializationDomain.Default.BsonDefaults.MaxDocumentSize;
+ set => BsonSerializationDomain.Default.BsonDefaults.MaxDocumentSize = value;
}
///
@@ -87,8 +62,8 @@ public static int MaxDocumentSize
///
public static int MaxSerializationDepth
{
- get { return __maxSerializationDepth; }
- set { __maxSerializationDepth = value; }
+ get => BsonSerializationDomain.Default.BsonDefaults.MaxSerializationDepth;
+ set => BsonSerializationDomain.Default.BsonDefaults.MaxSerializationDepth = value;
}
}
}
diff --git a/src/MongoDB.Bson/BsonDefaultsRegistry.cs b/src/MongoDB.Bson/BsonDefaultsRegistry.cs
new file mode 100644
index 00000000000..f7aae832128
--- /dev/null
+++ b/src/MongoDB.Bson/BsonDefaultsRegistry.cs
@@ -0,0 +1,73 @@
+/* Copyright 2010-present MongoDB Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Collections.Generic;
+using System.Dynamic;
+using MongoDB.Bson.Serialization;
+
+namespace MongoDB.Bson
+{
+ internal class BsonDefaultsRegistry : IBsonDefaults
+ {
+ private IBsonSerializationDomain _serializationDomain;
+ private bool _dynamicArraySerializerWasSet;
+ private IBsonSerializer _dynamicArraySerializer;
+ private bool _dynamicDocumentSerializerWasSet;
+ private IBsonSerializer _dynamicDocumentSerializer;
+
+ public BsonDefaultsRegistry(IBsonSerializationDomain serializationDomain)
+ {
+ _serializationDomain = serializationDomain;
+ }
+
+ public IBsonSerializer DynamicArraySerializer
+ {
+ get
+ {
+ if (!_dynamicArraySerializerWasSet)
+ {
+ _dynamicArraySerializer = _serializationDomain.LookupSerializer>();
+ }
+ return _dynamicArraySerializer;
+ }
+ set
+ {
+ _dynamicArraySerializerWasSet = true;
+ _dynamicArraySerializer = value;
+ }
+ }
+
+ public IBsonSerializer DynamicDocumentSerializer
+ {
+ get
+ {
+ if (!_dynamicDocumentSerializerWasSet)
+ {
+ _dynamicDocumentSerializer = _serializationDomain.LookupSerializer();
+ }
+ return _dynamicDocumentSerializer;
+ }
+ set
+ {
+ _dynamicDocumentSerializerWasSet = true;
+ _dynamicDocumentSerializer = value;
+ }
+ }
+
+ public int MaxDocumentSize { get; set; } = int.MaxValue;
+
+ public int MaxSerializationDepth { get; set; } = 100;
+ }
+}
diff --git a/src/MongoDB.Bson/BsonExtensionMethods.cs b/src/MongoDB.Bson/BsonExtensionMethods.cs
index bcb41a8271f..edb285caa3a 100644
--- a/src/MongoDB.Bson/BsonExtensionMethods.cs
+++ b/src/MongoDB.Bson/BsonExtensionMethods.cs
@@ -84,7 +84,7 @@ public static byte[] ToBson(
if (serializer == null)
{
- serializer = BsonSerializer.LookupSerializer(nominalType);
+ serializer = BsonSerializationDomain.Default.LookupSerializer(nominalType);
}
if (serializer.ValueType != nominalType)
{
@@ -165,7 +165,7 @@ public static BsonDocument ToBsonDocument(
return convertibleToBsonDocument.ToBsonDocument(); // use the provided ToBsonDocument method
}
- serializer = BsonSerializer.LookupSerializer(nominalType);
+ serializer = BsonSerializationDomain.Default.LookupSerializer(nominalType);
}
if (serializer.ValueType != nominalType)
{
@@ -236,7 +236,7 @@ public static string ToJson(
if (serializer == null)
{
- serializer = BsonSerializer.LookupSerializer(nominalType);
+ serializer = BsonSerializationDomain.Default.LookupSerializer(nominalType);
}
if (serializer.ValueType != nominalType)
{
diff --git a/src/MongoDB.Bson/IBsonDefaults.cs b/src/MongoDB.Bson/IBsonDefaults.cs
new file mode 100644
index 00000000000..e843c8eb4a4
--- /dev/null
+++ b/src/MongoDB.Bson/IBsonDefaults.cs
@@ -0,0 +1,39 @@
+/* Copyright 2010-present MongoDB Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using MongoDB.Bson.Serialization;
+
+namespace MongoDB.Bson
+{
+ internal interface IBsonDefaults
+ {
+ ///
+ ///
+ ///
+ IBsonSerializer DynamicArraySerializer { get; set; }
+ ///
+ ///
+ ///
+ IBsonSerializer DynamicDocumentSerializer { get; set; }
+ ///
+ ///
+ ///
+ int MaxDocumentSize { get; set; }
+ ///
+ ///
+ ///
+ int MaxSerializationDepth { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/MongoDB.Bson/IO/BsonBinaryReaderSettings.cs b/src/MongoDB.Bson/IO/BsonBinaryReaderSettings.cs
index 1c510ed71e3..89b1c4d1892 100644
--- a/src/MongoDB.Bson/IO/BsonBinaryReaderSettings.cs
+++ b/src/MongoDB.Bson/IO/BsonBinaryReaderSettings.cs
@@ -15,6 +15,7 @@
using System;
using System.Text;
+using MongoDB.Bson.Serialization;
namespace MongoDB.Bson.IO
{
@@ -30,7 +31,7 @@ public class BsonBinaryReaderSettings : BsonReaderSettings
private UTF8Encoding _encoding = Utf8Encodings.Strict;
private bool _fixOldBinarySubTypeOnInput = true;
private bool _fixOldDateTimeMaxValueOnInput = true;
- private int _maxDocumentSize = BsonDefaults.MaxDocumentSize;
+ private int _maxDocumentSize = BsonSerializationDomain.Default.BsonDefaults.MaxDocumentSize;
// constructors
///
diff --git a/src/MongoDB.Bson/IO/BsonBinaryWriterSettings.cs b/src/MongoDB.Bson/IO/BsonBinaryWriterSettings.cs
index 96c8f3168de..10a337bac29 100644
--- a/src/MongoDB.Bson/IO/BsonBinaryWriterSettings.cs
+++ b/src/MongoDB.Bson/IO/BsonBinaryWriterSettings.cs
@@ -15,6 +15,7 @@
using System;
using System.Text;
+using MongoDB.Bson.Serialization;
namespace MongoDB.Bson.IO
{
@@ -29,7 +30,7 @@ public class BsonBinaryWriterSettings : BsonWriterSettings
// private fields
private UTF8Encoding _encoding = Utf8Encodings.Strict;
private bool _fixOldBinarySubTypeOnOutput = true;
- private int _maxDocumentSize = BsonDefaults.MaxDocumentSize;
+ private int _maxDocumentSize = BsonSerializationDomain.Default.BsonDefaults.MaxDocumentSize;
// constructors
///
diff --git a/src/MongoDB.Bson/IO/BsonWriterSettings.cs b/src/MongoDB.Bson/IO/BsonWriterSettings.cs
index 06957993d70..f85cab99da5 100644
--- a/src/MongoDB.Bson/IO/BsonWriterSettings.cs
+++ b/src/MongoDB.Bson/IO/BsonWriterSettings.cs
@@ -14,6 +14,7 @@
*/
using System;
+using MongoDB.Bson.Serialization;
namespace MongoDB.Bson.IO
{
@@ -24,7 +25,7 @@ public abstract class BsonWriterSettings
{
// private fields
private bool _isFrozen;
- private int _maxSerializationDepth = BsonDefaults.MaxSerializationDepth;
+ private int _maxSerializationDepth = BsonSerializationDomain.Default.BsonDefaults.MaxSerializationDepth;
// constructors
///
diff --git a/src/MongoDB.Bson/ObjectModel/BsonDocumentWrapper.cs b/src/MongoDB.Bson/ObjectModel/BsonDocumentWrapper.cs
index 09edc32dbfa..92d507b1ec2 100644
--- a/src/MongoDB.Bson/ObjectModel/BsonDocumentWrapper.cs
+++ b/src/MongoDB.Bson/ObjectModel/BsonDocumentWrapper.cs
@@ -104,7 +104,12 @@ public static BsonDocumentWrapper Create(TNominalType value)
/// A BsonDocumentWrapper.
public static BsonDocumentWrapper Create(Type nominalType, object value)
{
- var serializer = BsonSerializer.LookupSerializer(nominalType);
+ return Create(BsonSerializationDomain.Default, nominalType, value);
+ }
+
+ internal static BsonDocumentWrapper Create(IBsonSerializationDomain serializationDomain, Type nominalType, object value)
+ {
+ var serializer = serializationDomain.LookupSerializer(nominalType);
return new BsonDocumentWrapper(value, serializer);
}
@@ -116,12 +121,21 @@ public static BsonDocumentWrapper Create(Type nominalType, object value)
/// A list of BsonDocumentWrappers.
public static IEnumerable CreateMultiple(IEnumerable values)
{
+ return CreateMultiple(BsonSerializationDomain.Default, values);
+ }
+
+ internal static IEnumerable CreateMultiple(IBsonSerializationDomain serializationDomain, IEnumerable values)
+ {
+ if (serializationDomain == null)
+ {
+ throw new ArgumentNullException("serializationDomain");
+ }
if (values == null)
{
throw new ArgumentNullException("values");
}
- var serializer = BsonSerializer.LookupSerializer(typeof(TNominalType));
+ var serializer = serializationDomain.LookupSerializer(typeof(TNominalType));
return values.Select(v => new BsonDocumentWrapper(v, serializer));
}
@@ -132,6 +146,11 @@ public static IEnumerable CreateMultiple(IEnu
/// A list of wrapped objects.
/// A list of BsonDocumentWrappers.
public static IEnumerable CreateMultiple(Type nominalType, IEnumerable values)
+ {
+ return CreateMultiple(BsonSerializationDomain.Default, nominalType, values);
+ }
+
+ internal static IEnumerable CreateMultiple(IBsonSerializationDomain serializationDomain, Type nominalType, IEnumerable values)
{
if (nominalType == null)
{
@@ -142,7 +161,7 @@ public static IEnumerable CreateMultiple(Type nominalType,
throw new ArgumentNullException("values");
}
- var serializer = BsonSerializer.LookupSerializer(nominalType);
+ var serializer = serializationDomain.LookupSerializer(nominalType);
return values.Cast
/// The class type.
public BsonClassMap(Type classType)
+ : this(BsonSerializationDomain.Default, classType)
{
+ }
+
+ internal BsonClassMap(IBsonSerializationDomain serializationDomain, Type classType)
+ {
+ _serializationDomain = serializationDomain;
_classType = classType;
_creatorMaps = new List();
_conventionPack = ConventionRegistry.Lookup(classType);
@@ -86,7 +88,13 @@ public BsonClassMap(Type classType)
/// Type of the class.
/// The base class map.
public BsonClassMap(Type classType, BsonClassMap baseClassMap)
- : this(classType)
+ : this(BsonSerializationDomain.Default, classType, baseClassMap)
+ {
+ throw new InvalidOperationException();
+ }
+
+ internal BsonClassMap(IBsonSerializationDomain serializationDomain, Type classType, BsonClassMap baseClassMap)
+ : this(serializationDomain, classType)
{
_baseClassMap = baseClassMap;
}
@@ -253,132 +261,41 @@ internal int ExtraElementsMemberMapIndex
get { return _extraElementsMemberIndex; }
}
- // public static methods
- ///
- /// Gets the type of a member.
- ///
- /// The member info.
- /// The type of the member.
- public static Type GetMemberInfoType(MemberInfo memberInfo)
- {
- if (memberInfo == null)
- {
- throw new ArgumentNullException("memberInfo");
- }
+ internal IBsonSerializationDomain SerializationDomain => _serializationDomain;
- if (memberInfo is FieldInfo)
- {
- return ((FieldInfo)memberInfo).FieldType;
- }
- else if (memberInfo is PropertyInfo)
- {
- return ((PropertyInfo)memberInfo).PropertyType;
- }
-
- throw new NotSupportedException("Only field and properties are supported at this time.");
- }
+ IBsonSerializationDomain IHasSerializationDomain.SerializationDomain => _serializationDomain;
+ // public static methods
///
/// Gets all registered class maps.
///
/// All registered class maps.
- public static IEnumerable GetRegisteredClassMaps()
- {
- BsonSerializer.ConfigLock.EnterReadLock();
- try
- {
- return __classMaps.Values.ToList(); // return a copy for thread safety
- }
- finally
- {
- BsonSerializer.ConfigLock.ExitReadLock();
- }
- }
+ public static IEnumerable GetRegisteredClassMaps() =>
+ BsonSerializationDomain.Default.ClassMapRegistry.GetRegisteredClassMaps();
///
/// Checks whether a class map is registered for a type.
///
/// The type to check.
/// True if there is a class map registered for the type.
- public static bool IsClassMapRegistered(Type type)
- {
- if (type == null)
- {
- throw new ArgumentNullException("type");
- }
-
- BsonSerializer.ConfigLock.EnterReadLock();
- try
- {
- return __classMaps.ContainsKey(type);
- }
- finally
- {
- BsonSerializer.ConfigLock.ExitReadLock();
- }
- }
+ public static bool IsClassMapRegistered(Type type) =>
+ BsonSerializationDomain.Default.ClassMapRegistry.IsClassMapRegistered(type);
///
/// Looks up a class map (will AutoMap the class if no class map is registered).
///
/// The class type.
/// The class map.
- public static BsonClassMap LookupClassMap(Type classType)
- {
- if (classType == null)
- {
- throw new ArgumentNullException("classType");
- }
-
- BsonSerializer.ConfigLock.EnterReadLock();
- try
- {
- if (__classMaps.TryGetValue(classType, out var classMap))
- {
- if (classMap.IsFrozen)
- {
- return classMap;
- }
- }
- }
- finally
- {
- BsonSerializer.ConfigLock.ExitReadLock();
- }
-
- // automatically create a new classMap for classType and register it (unless another thread does first)
- // do the work of speculatively creating a new class map outside of holding any lock
- var classMapDefinition = typeof(BsonClassMap<>);
- var classMapType = classMapDefinition.MakeGenericType(classType);
- var newClassMap = (BsonClassMap)Activator.CreateInstance(classMapType);
- newClassMap.AutoMap();
-
- BsonSerializer.ConfigLock.EnterWriteLock();
- try
- {
- if (!__classMaps.TryGetValue(classType, out var classMap))
- {
- RegisterClassMap(newClassMap);
- classMap = newClassMap;
- }
-
- return classMap.Freeze();
- }
- finally
- {
- BsonSerializer.ConfigLock.ExitWriteLock();
- }
- }
+ public static BsonClassMap LookupClassMap(Type classType) =>
+ BsonSerializationDomain.Default.ClassMapRegistry.LookupClassMap(classType);
///
/// Creates and registers a class map.
///
/// The class.
/// The class map.
- public static BsonClassMap RegisterClassMap()
- {
- return RegisterClassMap(cm => { cm.AutoMap(); });
- }
+ public static BsonClassMap RegisterClassMap()=>
+ BsonSerializationDomain.Default.ClassMapRegistry.RegisterClassMap();
///
/// Creates and registers a class map.
@@ -387,35 +304,14 @@ public static BsonClassMap RegisterClassMap()
/// The class map initializer.
/// The class map.
public static BsonClassMap RegisterClassMap(Action> classMapInitializer)
- {
- var classMap = new BsonClassMap(classMapInitializer);
- RegisterClassMap(classMap);
- return classMap;
- }
+ => BsonSerializationDomain.Default.ClassMapRegistry.RegisterClassMap(classMapInitializer);
///
/// Registers a class map.
///
/// The class map.
public static void RegisterClassMap(BsonClassMap classMap)
- {
- if (classMap == null)
- {
- throw new ArgumentNullException("classMap");
- }
-
- BsonSerializer.ConfigLock.EnterWriteLock();
- try
- {
- // note: class maps can NOT be replaced (because derived classes refer to existing instance)
- __classMaps.Add(classMap.ClassType, classMap);
- BsonSerializer.RegisterDiscriminator(classMap.ClassType, classMap.Discriminator);
- }
- finally
- {
- BsonSerializer.ConfigLock.ExitWriteLock();
- }
- }
+ => BsonSerializationDomain.Default.ClassMapRegistry.RegisterClassMap(classMap);
///
/// Registers a class map if it is not already registered.
@@ -423,16 +319,7 @@ public static void RegisterClassMap(BsonClassMap classMap)
/// The class.
/// True if this call registered the class map, false if the class map was already registered.
public static bool TryRegisterClassMap()
- {
- return TryRegisterClassMap(ClassMapFactory);
-
- static BsonClassMap ClassMapFactory()
- {
- var classMap = new BsonClassMap();
- classMap.AutoMap();
- return classMap;
- }
- }
+ => BsonSerializationDomain.Default.ClassMapRegistry.TryRegisterClassMap();
///
/// Registers a class map if it is not already registered.
@@ -441,19 +328,7 @@ static BsonClassMap ClassMapFactory()
/// The class map.
/// True if this call registered the class map, false if the class map was already registered.
public static bool TryRegisterClassMap(BsonClassMap classMap)
- {
- if (classMap == null)
- {
- throw new ArgumentNullException(nameof(classMap));
- }
-
- return TryRegisterClassMap(ClassMapFactory);
-
- BsonClassMap ClassMapFactory()
- {
- return classMap;
- }
- }
+ => BsonSerializationDomain.Default.ClassMapRegistry.TryRegisterClassMap(classMap);
///
/// Registers a class map if it is not already registered.
@@ -462,19 +337,7 @@ BsonClassMap ClassMapFactory()
/// The class map initializer (only called if the class map is not already registered).
/// True if this call registered the class map, false if the class map was already registered.
public static bool TryRegisterClassMap(Action> classMapInitializer)
- {
- if (classMapInitializer == null)
- {
- throw new ArgumentNullException(nameof(classMapInitializer));
- }
-
- return TryRegisterClassMap(ClassMapFactory);
-
- BsonClassMap ClassMapFactory()
- {
- return new BsonClassMap(classMapInitializer);
- }
- }
+ => BsonSerializationDomain.Default.ClassMapRegistry.TryRegisterClassMap(classMapInitializer);
///
/// Registers a class map if it is not already registered.
@@ -483,45 +346,7 @@ BsonClassMap ClassMapFactory()
/// The class map factory (only called if the class map is not already registered).
/// True if this call registered the class map, false if the class map was already registered.
public static bool TryRegisterClassMap(Func> classMapFactory)
- {
- if (classMapFactory == null)
- {
- throw new ArgumentNullException(nameof(classMapFactory));
- }
-
- BsonSerializer.ConfigLock.EnterReadLock();
- try
- {
- if (__classMaps.ContainsKey(typeof(TClass)))
- {
- return false;
- }
- }
- finally
- {
- BsonSerializer.ConfigLock.ExitReadLock();
- }
-
- BsonSerializer.ConfigLock.EnterWriteLock();
- try
- {
- if (__classMaps.ContainsKey(typeof(TClass)))
- {
- return false;
- }
- else
- {
- // create a classMap for TClass and register it
- var classMap = classMapFactory();
- RegisterClassMap(classMap);
- return true;
- }
- }
- finally
- {
- BsonSerializer.ConfigLock.ExitWriteLock();
- }
- }
+ => BsonSerializationDomain.Default.ClassMapRegistry.TryRegisterClassMap(classMapFactory);
// public methods
///
@@ -573,13 +398,29 @@ obj is BsonClassMap other &&
///
public override int GetHashCode() => 0;
+ internal class FreezeContext
+ {
+ public int FreezeNestingLevel { get; set; } = 0;
+ public Queue KnownTypesQueue { get; set; } = new();
+ public IBsonSerializationDomain SerializationDomain { get; set; }
+ }
+
///
/// Freezes the class map.
///
/// The frozen class map.
- public BsonClassMap Freeze()
+ public BsonClassMap Freeze() => Freeze(BsonSerializationDomain.Default);
+
+ internal BsonClassMap Freeze(IBsonSerializationDomain serializationDomain)
+ {
+ var freezeContext = new FreezeContext { SerializationDomain = serializationDomain };
+ return Freeze(freezeContext);
+ }
+
+ private BsonClassMap Freeze(FreezeContext context)
{
- BsonSerializer.ConfigLock.EnterReadLock();
+ var configLock = context.SerializationDomain!.ConfigLock;
+ configLock.EnterReadLock();
try
{
if (_frozen)
@@ -589,15 +430,15 @@ public BsonClassMap Freeze()
}
finally
{
- BsonSerializer.ConfigLock.ExitReadLock();
+ configLock.ExitReadLock();
}
- BsonSerializer.ConfigLock.EnterWriteLock();
+ configLock.EnterWriteLock();
try
{
if (!_frozen)
{
- __freezeNestingLevel++;
+ context.FreezeNestingLevel++;
try
{
var baseType = _classType.GetTypeInfo().BaseType;
@@ -605,9 +446,9 @@ public BsonClassMap Freeze()
{
if (_baseClassMap == null)
{
- _baseClassMap = LookupClassMap(baseType);
+ _baseClassMap = context.SerializationDomain.ClassMapRegistry.LookupClassMap(baseType);
}
- _baseClassMap.Freeze();
+ _baseClassMap.Freeze(context);
_discriminatorIsRequired |= _baseClassMap._discriminatorIsRequired;
_hasRootClass |= (_isRootClass || _baseClassMap.HasRootClass);
_allMemberMaps.AddRange(_baseClassMap.AllMemberMaps);
@@ -699,28 +540,28 @@ public BsonClassMap Freeze()
// this avoids infinite recursion when going back down the inheritance tree while processing known types
foreach (var knownType in _knownTypes)
{
- __knownTypesQueue.Enqueue(knownType);
+ context.KnownTypesQueue.Enqueue(knownType);
}
// if we are back to the first level go ahead and process any queued known types
- if (__freezeNestingLevel == 1)
+ if (context.FreezeNestingLevel == 1)
{
- while (__knownTypesQueue.Count != 0)
+ while (context.KnownTypesQueue.Count != 0)
{
- var knownType = __knownTypesQueue.Dequeue();
- LookupClassMap(knownType); // will AutoMap and/or Freeze knownType if necessary
+ var knownType = context.KnownTypesQueue.Dequeue();
+ context.SerializationDomain.ClassMapRegistry.LookupClassMap(knownType); // will AutoMap and/or Freeze knownType if necessary
}
}
}
finally
{
- __freezeNestingLevel--;
+ context.FreezeNestingLevel--;
}
}
}
finally
{
- BsonSerializer.ConfigLock.ExitWriteLock();
+ configLock.ExitWriteLock();
}
return this;
}
@@ -1019,7 +860,7 @@ public BsonMemberMap MapMember(MemberInfo memberInfo)
var memberMap = _declaredMemberMaps.Find(m => m.MemberInfo == memberInfo);
if (memberMap == null)
{
- memberMap = new BsonMemberMap(this, memberInfo);
+ memberMap = new BsonMemberMap(_serializationDomain, this, memberInfo);
_declaredMemberMaps.Add(memberMap);
}
return memberMap;
@@ -1354,7 +1195,7 @@ IDiscriminatorConvention LookupDiscriminatorConvention()
return classMap._discriminatorConvention;
}
- if (BsonSerializer.IsDiscriminatorConventionRegisteredAtThisLevel(classMap._classType))
+ if (_serializationDomain.IsDiscriminatorConventionRegisteredAtThisLevel(classMap._classType))
{
// in this case LookupDiscriminatorConvention below will find it
break;
@@ -1363,21 +1204,23 @@ IDiscriminatorConvention LookupDiscriminatorConvention()
if (classMap._isRootClass)
{
// in this case auto-register a hierarchical convention for the root class and look it up as usual below
- BsonSerializer.GetOrRegisterDiscriminatorConvention(classMap._classType, StandardDiscriminatorConvention.Hierarchical);
+ var discriminatorConvention = new HierarchicalDiscriminatorConvention(_serializationDomain, "_t");
+ _serializationDomain.GetOrRegisterDiscriminatorConvention(classMap._classType, discriminatorConvention);
break;
}
classMap = classMap._baseClassMap;
}
- return BsonSerializer.LookupDiscriminatorConvention(_classType);
+ return _serializationDomain.LookupDiscriminatorConvention(_classType);
}
}
// private methods
private void AutoMapClass()
{
- new ConventionRunner(_conventionPack).Apply(this);
+ var conventionPack = _serializationDomain.ConventionRegistry.Lookup(_classType);
+ new ConventionRunner(conventionPack).Apply(this);
foreach (var memberMap in _declaredMemberMaps)
{
@@ -1484,7 +1327,12 @@ public class BsonClassMap : BsonClassMap
/// Initializes a new instance of the BsonClassMap class.
///
public BsonClassMap()
- : base(typeof(TClass))
+ : this(BsonSerializationDomain.Default)
+ {
+ }
+
+ internal BsonClassMap(IBsonSerializationDomain serializationDomain)
+ : base(serializationDomain, typeof(TClass))
{
}
@@ -1493,7 +1341,12 @@ public BsonClassMap()
///
/// The class map initializer.
public BsonClassMap(Action> classMapInitializer)
- : base(typeof(TClass))
+ : this(BsonSerializationDomain.Default, classMapInitializer)
+ {
+ }
+
+ internal BsonClassMap(IBsonSerializationDomain serializationDomain, Action> classMapInitializer)
+ : base(serializationDomain, typeof(TClass))
{
classMapInitializer(this);
}
@@ -1503,7 +1356,12 @@ public BsonClassMap(Action> classMapInitializer)
///
/// The base class map.
public BsonClassMap(BsonClassMap baseClassMap)
- : base(typeof(TClass), baseClassMap)
+ : this(BsonSerializationDomain.Default, baseClassMap)
+ {
+ }
+
+ internal BsonClassMap(IBsonSerializationDomain serializationDomain, BsonClassMap baseClassMap)
+ : base(serializationDomain, typeof(TClass), baseClassMap)
{
}
diff --git a/src/MongoDB.Bson/Serialization/BsonClassMapRegistry.cs b/src/MongoDB.Bson/Serialization/BsonClassMapRegistry.cs
new file mode 100644
index 00000000000..bbc0930efdf
--- /dev/null
+++ b/src/MongoDB.Bson/Serialization/BsonClassMapRegistry.cs
@@ -0,0 +1,286 @@
+/* Copyright 2010-present MongoDB Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace MongoDB.Bson.Serialization;
+
+internal class BsonClassMapRegistry : IBsonClassMapRegistry, IHasSerializationDomain
+{
+ // private fields
+ private readonly Dictionary _classMaps = new();
+ private readonly IBsonSerializationDomain _serializationDomain;
+
+ public BsonClassMapRegistry(BsonSerializationDomain serializationDomain)
+ {
+ _serializationDomain = serializationDomain;
+ }
+
+ IBsonSerializationDomain IHasSerializationDomain.SerializationDomain => _serializationDomain;
+
+ ///
+ /// Gets all registered class maps.
+ ///
+ /// All registered class maps.
+ public IEnumerable GetRegisteredClassMaps()
+ {
+ _serializationDomain.ConfigLock.EnterReadLock();
+ try
+ {
+ return _classMaps.Values.ToList(); // return a copy for thread safety
+ }
+ finally
+ {
+ _serializationDomain.ConfigLock.ExitReadLock();
+ }
+ }
+
+ ///
+ /// Checks whether a class map is registered for a type.
+ ///
+ /// The type to check.
+ /// True if there is a class map registered for the type.
+ public bool IsClassMapRegistered(Type type)
+ {
+ if (type == null)
+ {
+ throw new ArgumentNullException("type");
+ }
+
+ _serializationDomain.ConfigLock.EnterReadLock();
+ try
+ {
+ return _classMaps.ContainsKey(type);
+ }
+ finally
+ {
+ _serializationDomain.ConfigLock.ExitReadLock();
+ }
+ }
+
+ ///
+ /// Looks up a class map (will AutoMap the class if no class map is registered).
+ ///
+ /// The class type.
+ /// The class map.
+ public BsonClassMap LookupClassMap(Type classType)
+ {
+ if (classType == null)
+ {
+ throw new ArgumentNullException("classType");
+ }
+
+ _serializationDomain.ConfigLock.EnterReadLock();
+ try
+ {
+ if (_classMaps.TryGetValue(classType, out var classMap))
+ {
+ if (classMap.IsFrozen)
+ {
+ return classMap;
+ }
+ }
+ }
+ finally
+ {
+ _serializationDomain.ConfigLock.ExitReadLock();
+ }
+
+ // automatically create a new classMap for classType and register it (unless another thread does first)
+ // do the work of speculatively creating a new class map outside of holding any lock
+ var classMapDefinition = typeof(BsonClassMap<>);
+ var classMapType = classMapDefinition.MakeGenericType(classType);
+ var bindingAttr = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
+ var newClassMap = (BsonClassMap)Activator.CreateInstance(classMapType, bindingAttr, binder: null, args: [_serializationDomain], culture: null);
+ newClassMap.AutoMap();
+
+ _serializationDomain.ConfigLock.EnterWriteLock();
+ try
+ {
+ if (!_classMaps.TryGetValue(classType, out var classMap))
+ {
+ RegisterClassMap(newClassMap);
+ classMap = newClassMap;
+ }
+
+ return classMap.Freeze(_serializationDomain);
+ }
+ finally
+ {
+ _serializationDomain.ConfigLock.ExitWriteLock();
+ }
+ }
+
+ ///
+ /// Creates and registers a class map.
+ ///
+ /// The class.
+ /// The class map.
+ public BsonClassMap RegisterClassMap()
+ {
+ return RegisterClassMap(cm => { cm.AutoMap(); });
+ }
+
+ ///
+ /// Creates and registers a class map.
+ ///
+ /// The class.
+ /// The class map initializer.
+ /// The class map.
+ public BsonClassMap RegisterClassMap(Action> classMapInitializer)
+ {
+ var classMap = new BsonClassMap(_serializationDomain, classMapInitializer);
+ RegisterClassMap(classMap);
+ return classMap;
+ }
+
+ ///
+ /// Registers a class map.
+ ///
+ /// The class map.
+ public void RegisterClassMap(BsonClassMap classMap)
+ {
+ if (classMap == null)
+ {
+ throw new ArgumentNullException("classMap");
+ }
+
+ if (classMap.SerializationDomain != _serializationDomain)
+ {
+ throw new ArgumentException($"Expected class map to be for serialization domain {_serializationDomain.Name}, but was for serialization domain {classMap.SerializationDomain.Name}.");
+ }
+
+ _serializationDomain.ConfigLock.EnterWriteLock();
+ try
+ {
+ // note: class maps can NOT be replaced (because derived classes refer to existing instance)
+ _classMaps.Add(classMap.ClassType, classMap);
+ _serializationDomain.RegisterDiscriminator(classMap.ClassType, classMap.Discriminator);
+ }
+ finally
+ {
+ _serializationDomain.ConfigLock.ExitWriteLock();
+ }
+ }
+
+ ///
+ /// Registers a class map if it is not already registered.
+ ///
+ /// The class.
+ /// True if this call registered the class map, false if the class map was already registered.
+ public bool TryRegisterClassMap()
+ {
+ return TryRegisterClassMap(() => ClassMapFactory());
+
+ BsonClassMap ClassMapFactory()
+ {
+ var classMap = new BsonClassMap(_serializationDomain);
+ classMap.AutoMap();
+ return classMap;
+ }
+ }
+
+ ///
+ /// Registers a class map if it is not already registered.
+ ///
+ /// The class.
+ /// The class map.
+ /// True if this call registered the class map, false if the class map was already registered.
+ public bool TryRegisterClassMap(BsonClassMap classMap)
+ {
+ if (classMap == null)
+ {
+ throw new ArgumentNullException(nameof(classMap));
+ }
+
+ return TryRegisterClassMap(ClassMapFactory);
+
+ BsonClassMap ClassMapFactory()
+ {
+ return classMap;
+ }
+ }
+
+ ///
+ /// Registers a class map if it is not already registered.
+ ///
+ /// The class.
+ /// The class map initializer (only called if the class map is not already registered).
+ /// True if this call registered the class map, false if the class map was already registered.
+ public bool TryRegisterClassMap(Action> classMapInitializer)
+ {
+ if (classMapInitializer == null)
+ {
+ throw new ArgumentNullException(nameof(classMapInitializer));
+ }
+
+ return TryRegisterClassMap(ClassMapFactory);
+
+ BsonClassMap ClassMapFactory()
+ {
+ return new BsonClassMap(classMapInitializer);
+ }
+ }
+
+ ///
+ /// Registers a class map if it is not already registered.
+ ///
+ /// The class.
+ /// The class map factory (only called if the class map is not already registered).
+ /// True if this call registered the class map, false if the class map was already registered.
+ public bool TryRegisterClassMap(Func> classMapFactory)
+ {
+ if (classMapFactory == null)
+ {
+ throw new ArgumentNullException(nameof(classMapFactory));
+ }
+
+ _serializationDomain.ConfigLock.EnterReadLock();
+ try
+ {
+ if (_classMaps.ContainsKey(typeof(TClass)))
+ {
+ return false;
+ }
+ }
+ finally
+ {
+ _serializationDomain.ConfigLock.ExitReadLock();
+ }
+
+ _serializationDomain.ConfigLock.EnterWriteLock();
+ try
+ {
+ if (_classMaps.ContainsKey(typeof(TClass)))
+ {
+ return false;
+ }
+ else
+ {
+ // create a classMap for TClass and register it
+ var classMap = classMapFactory();
+ RegisterClassMap(classMap);
+ return true;
+ }
+ }
+ finally
+ {
+ _serializationDomain.ConfigLock.ExitWriteLock();
+ }
+ }
+}
diff --git a/src/MongoDB.Bson/Serialization/BsonClassMapSerializationProvider.cs b/src/MongoDB.Bson/Serialization/BsonClassMapSerializationProvider.cs
index ac088360c2d..c0bc1952598 100644
--- a/src/MongoDB.Bson/Serialization/BsonClassMapSerializationProvider.cs
+++ b/src/MongoDB.Bson/Serialization/BsonClassMapSerializationProvider.cs
@@ -21,8 +21,17 @@ namespace MongoDB.Bson.Serialization
///
/// Represents the class map serialization provider.
///
- internal class BsonClassMapSerializationProvider : BsonSerializationProviderBase
+ internal class BsonClassMapSerializationProvider : BsonSerializationProviderBase, IHasSerializationDomain
{
+ private readonly IBsonSerializationDomain _serializationDomain;
+
+ public BsonClassMapSerializationProvider(IBsonSerializationDomain serializationDomain)
+ {
+ _serializationDomain = serializationDomain;
+ }
+
+ public IBsonSerializationDomain SerializationDomain { get; }
+
///
public override IBsonSerializer GetSerializer(Type type, IBsonSerializerRegistry serializerRegistry)
{
@@ -41,10 +50,11 @@ public override IBsonSerializer GetSerializer(Type type, IBsonSerializerRegistry
!typeof(Array).GetTypeInfo().IsAssignableFrom(type) &&
!typeof(Enum).GetTypeInfo().IsAssignableFrom(type))
{
- var classMap = BsonClassMap.LookupClassMap(type);
+ var classMap = _serializationDomain.ClassMapRegistry.LookupClassMap(type);
var classMapSerializerDefinition = typeof(BsonClassMapSerializer<>);
var classMapSerializerType = classMapSerializerDefinition.MakeGenericType(type);
- return (IBsonSerializer)Activator.CreateInstance(classMapSerializerType, classMap);
+ var bindingAttr = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
+ return (IBsonSerializer)Activator.CreateInstance(classMapSerializerType, bindingAttr, binder: null, args: [_serializationDomain, classMap], culture: null);
}
return null;
diff --git a/src/MongoDB.Bson/Serialization/BsonMemberMap.cs b/src/MongoDB.Bson/Serialization/BsonMemberMap.cs
index b10971d5249..3fcd43a61b2 100644
--- a/src/MongoDB.Bson/Serialization/BsonMemberMap.cs
+++ b/src/MongoDB.Bson/Serialization/BsonMemberMap.cs
@@ -23,13 +23,14 @@ namespace MongoDB.Bson.Serialization
///
/// Represents the mapping between a field or property and a BSON element.
///
- public class BsonMemberMap
+ public class BsonMemberMap : IHasSerializationDomain
{
// private fields
private readonly BsonClassMap _classMap;
private readonly MemberInfo _memberInfo;
private readonly Type _memberType;
private readonly bool _memberTypeIsBsonValue;
+ private readonly IBsonSerializationDomain _serializationDomain;
private string _elementName;
private bool _frozen; // once a class map has been frozen no further changes are allowed
@@ -53,10 +54,16 @@ public class BsonMemberMap
/// The class map this member map belongs to.
/// The member info.
public BsonMemberMap(BsonClassMap classMap, MemberInfo memberInfo)
+ : this(classMap.SerializationDomain, classMap, memberInfo)
{
+ }
+
+ internal BsonMemberMap(IBsonSerializationDomain serializationDomain, BsonClassMap classMap, MemberInfo memberInfo)
+ {
+ _serializationDomain = serializationDomain;
_classMap = classMap;
_memberInfo = memberInfo;
- _memberType = BsonClassMap.GetMemberInfoType(memberInfo);
+ _memberType = GetMemberInfoType(memberInfo);
_memberTypeIsBsonValue = typeof(BsonValue).GetTypeInfo().IsAssignableFrom(_memberType);
Reset();
@@ -134,6 +141,8 @@ public Func