diff --git a/src/MongoDB.Bson/BsonDefaults.cs b/src/MongoDB.Bson/BsonDefaults.cs index 9352d3228ff..47c01789356 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 => BsonSerializer.DefaultSerializationDomain.BsonDefaults.DynamicArraySerializer; + set => BsonSerializer.DefaultSerializationDomain.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 => BsonSerializer.DefaultSerializationDomain.BsonDefaults.DynamicDocumentSerializer; + set => BsonSerializer.DefaultSerializationDomain.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 => BsonSerializer.DefaultSerializationDomain.BsonDefaults.MaxDocumentSize; + set => BsonSerializer.DefaultSerializationDomain.BsonDefaults.MaxDocumentSize = value; } /// @@ -87,8 +62,8 @@ public static int MaxDocumentSize /// public static int MaxSerializationDepth { - get { return __maxSerializationDepth; } - set { __maxSerializationDepth = value; } + get => BsonSerializer.DefaultSerializationDomain.BsonDefaults.MaxSerializationDepth; + set => BsonSerializer.DefaultSerializationDomain.BsonDefaults.MaxSerializationDepth = value; } } } diff --git a/src/MongoDB.Bson/BsonDefaultsDomain.cs b/src/MongoDB.Bson/BsonDefaultsDomain.cs new file mode 100644 index 00000000000..b105e739374 --- /dev/null +++ b/src/MongoDB.Bson/BsonDefaultsDomain.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 BsonDefaultsDomain : IBsonDefaults + { + private IBsonSerializationDomain _serializationDomain; + private bool _dynamicArraySerializerWasSet; + private IBsonSerializer _dynamicArraySerializer; + private bool _dynamicDocumentSerializerWasSet; + private IBsonSerializer _dynamicDocumentSerializer; + + public BsonDefaultsDomain(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; + } +} \ No newline at end of file diff --git a/src/MongoDB.Bson/BsonExtensionMethods.cs b/src/MongoDB.Bson/BsonExtensionMethods.cs index bcb41a8271f..bd52c52e832 100644 --- a/src/MongoDB.Bson/BsonExtensionMethods.cs +++ b/src/MongoDB.Bson/BsonExtensionMethods.cs @@ -25,6 +25,8 @@ namespace MongoDB.Bson /// public static class BsonExtensionMethods { + //DOMAIN-API We should remove this and use the version with the domain. + //QUESTION: Do we want to do something now about this...? It's used also internally, but it seems in most cases it's used for "default serialization", so it should be ok. /// /// Serializes an object to a BSON byte array. /// @@ -49,6 +51,21 @@ public static byte[] ToBson( return ToBson(obj, typeof(TNominalType), writerSettings, serializer, configurator, args, estimatedBsonSize); } + internal static byte[] ToBson( + this TNominalType obj, + IBsonSerializationDomain serializationDomain, + IBsonSerializer serializer = null, + BsonBinaryWriterSettings writerSettings = null, + Action configurator = null, + BsonSerializationArgs args = default(BsonSerializationArgs), + int estimatedBsonSize = 0) + { + args.SetOrValidateNominalType(typeof(TNominalType), ""); + + return ToBson(obj, typeof(TNominalType), serializationDomain, writerSettings, serializer, configurator, args, estimatedBsonSize); + } + + //DOMAIN-API We should remove this and use the version with the domain. /// /// Serializes an object to a BSON byte array. /// @@ -68,6 +85,17 @@ public static byte[] ToBson( BsonBinaryWriterSettings writerSettings = null, IBsonSerializer serializer = null, Action configurator = null, + BsonSerializationArgs args = default, + int estimatedBsonSize = 0) => ToBson(obj, nominalType, BsonSerializer.DefaultSerializationDomain, writerSettings, + serializer, configurator, args, estimatedBsonSize); + + internal static byte[] ToBson( + this object obj, + Type nominalType, + IBsonSerializationDomain serializationDomain, + BsonBinaryWriterSettings writerSettings = null, + IBsonSerializer serializer = null, + Action configurator = null, BsonSerializationArgs args = default(BsonSerializationArgs), int estimatedBsonSize = 0) { @@ -84,7 +112,7 @@ public static byte[] ToBson( if (serializer == null) { - serializer = BsonSerializer.LookupSerializer(nominalType); + serializer = serializationDomain.LookupSerializer(nominalType); } if (serializer.ValueType != nominalType) { @@ -96,7 +124,7 @@ public static byte[] ToBson( { using (var bsonWriter = new BsonBinaryWriter(memoryStream, writerSettings ?? BsonBinaryWriterSettings.Defaults)) { - var context = BsonSerializationContext.CreateRoot(bsonWriter, configurator); + var context = BsonSerializationContext.CreateRoot(bsonWriter, serializationDomain, configurator); serializer.Serialize(context, args, obj); } return memoryStream.ToArray(); @@ -138,7 +166,16 @@ public static BsonDocument ToBsonDocument( Type nominalType, IBsonSerializer serializer = null, Action configurator = null, - BsonSerializationArgs args = default(BsonSerializationArgs)) + BsonSerializationArgs args = default) => ToBsonDocument(obj, nominalType, + BsonSerializer.DefaultSerializationDomain, serializer, configurator, args); + + internal static BsonDocument ToBsonDocument( + this object obj, + Type nominalType, + IBsonSerializationDomain serializationDomain, + IBsonSerializer serializer = null, + Action configurator = null, + BsonSerializationArgs args = default) { if (nominalType == null) { @@ -165,7 +202,7 @@ public static BsonDocument ToBsonDocument( return convertibleToBsonDocument.ToBsonDocument(); // use the provided ToBsonDocument method } - serializer = BsonSerializer.LookupSerializer(nominalType); + serializer = serializationDomain.LookupSerializer(nominalType); } if (serializer.ValueType != nominalType) { @@ -177,7 +214,7 @@ public static BsonDocument ToBsonDocument( var document = new BsonDocument(); using (var bsonWriter = new BsonDocumentWriter(document)) { - var context = BsonSerializationContext.CreateRoot(bsonWriter, configurator); + var context = BsonSerializationContext.CreateRoot(bsonWriter, serializationDomain, configurator); serializer.Serialize(context, args, obj); } return document; @@ -226,6 +263,16 @@ public static string ToJson( JsonWriterSettings writerSettings = null, IBsonSerializer serializer = null, Action configurator = null, + BsonSerializationArgs args = default) + => ToJson(obj, nominalType, BsonSerializer.DefaultSerializationDomain, writerSettings, serializer, configurator, args); + + internal static string ToJson( + this object obj, + Type nominalType, + IBsonSerializationDomain domain, + JsonWriterSettings writerSettings = null, + IBsonSerializer serializer = null, + Action configurator = null, BsonSerializationArgs args = default(BsonSerializationArgs)) { if (nominalType == null) @@ -236,7 +283,7 @@ public static string ToJson( if (serializer == null) { - serializer = BsonSerializer.LookupSerializer(nominalType); + serializer = domain.LookupSerializer(nominalType); } if (serializer.ValueType != nominalType) { @@ -248,7 +295,7 @@ public static string ToJson( { using (var bsonWriter = new JsonWriter(stringWriter, writerSettings ?? JsonWriterSettings.Defaults)) { - var context = BsonSerializationContext.CreateRoot(bsonWriter, configurator); + var context = BsonSerializationContext.CreateRoot(bsonWriter, domain, configurator); serializer.Serialize(context, args, obj); } return stringWriter.ToString(); 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..3b1cc9f4a1e 100644 --- a/src/MongoDB.Bson/IO/BsonBinaryReaderSettings.cs +++ b/src/MongoDB.Bson/IO/BsonBinaryReaderSettings.cs @@ -136,7 +136,7 @@ protected override BsonReaderSettings CloneImplementation() Encoding = _encoding, FixOldBinarySubTypeOnInput = _fixOldBinarySubTypeOnInput, FixOldDateTimeMaxValueOnInput = _fixOldDateTimeMaxValueOnInput, - MaxDocumentSize = _maxDocumentSize + MaxDocumentSize = _maxDocumentSize, }; return clone; diff --git a/src/MongoDB.Bson/IO/BsonBinaryWriterSettings.cs b/src/MongoDB.Bson/IO/BsonBinaryWriterSettings.cs index 96c8f3168de..5ee1a8c8676 100644 --- a/src/MongoDB.Bson/IO/BsonBinaryWriterSettings.cs +++ b/src/MongoDB.Bson/IO/BsonBinaryWriterSettings.cs @@ -122,7 +122,7 @@ protected override BsonWriterSettings CloneImplementation() Encoding = _encoding, FixOldBinarySubTypeOnOutput = _fixOldBinarySubTypeOnOutput, MaxDocumentSize = _maxDocumentSize, - MaxSerializationDepth = MaxSerializationDepth + MaxSerializationDepth = MaxSerializationDepth, }; return clone; } diff --git a/src/MongoDB.Bson/IO/BsonReader.cs b/src/MongoDB.Bson/IO/BsonReader.cs index 25724b855e7..c1e119bccb0 100644 --- a/src/MongoDB.Bson/IO/BsonReader.cs +++ b/src/MongoDB.Bson/IO/BsonReader.cs @@ -321,13 +321,15 @@ public virtual IByteBuffer ReadRawBsonArray() // overridden in BsonBinaryReader to read the raw bytes from the stream // for all other streams, deserialize the array and reserialize it using a BsonBinaryWriter to get the raw bytes - var deserializationContext = BsonDeserializationContext.CreateRoot(this); + //QUESTION Is it correct we only need a default domain here? + var deserializationContext = BsonDeserializationContext.CreateRoot(this, BsonSerializer.DefaultSerializationDomain); var array = BsonArraySerializer.Instance.Deserialize(deserializationContext); using (var memoryStream = new MemoryStream()) using (var bsonWriter = new BsonBinaryWriter(memoryStream, BsonBinaryWriterSettings.Defaults)) { - var serializationContext = BsonSerializationContext.CreateRoot(bsonWriter); + //QUESTION Is it correct we only need a default domain here? + var serializationContext = BsonSerializationContext.CreateRoot(bsonWriter, BsonSerializer.DefaultSerializationDomain); bsonWriter.WriteStartDocument(); var startPosition = memoryStream.Position + 3; // just past BsonType, "x" and null byte bsonWriter.WriteName("x"); @@ -351,7 +353,8 @@ public virtual IByteBuffer ReadRawBsonDocument() // overridden in BsonBinaryReader to read the raw bytes from the stream // for all other streams, deserialize the document and use ToBson to get the raw bytes - var deserializationContext = BsonDeserializationContext.CreateRoot(this); + //QUESTION Is it correct we only need a default domain here? + var deserializationContext = BsonDeserializationContext.CreateRoot(this, BsonSerializer.DefaultSerializationDomain); var document = BsonDocumentSerializer.Instance.Deserialize(deserializationContext); var bytes = document.ToBson(); return new ByteArrayBuffer(bytes, isReadOnly: true); diff --git a/src/MongoDB.Bson/IO/BsonReaderSettings.cs b/src/MongoDB.Bson/IO/BsonReaderSettings.cs index 649c02ff59c..61992ad4c0d 100644 --- a/src/MongoDB.Bson/IO/BsonReaderSettings.cs +++ b/src/MongoDB.Bson/IO/BsonReaderSettings.cs @@ -14,6 +14,7 @@ */ using System; +using MongoDB.Bson.Serialization; namespace MongoDB.Bson.IO { diff --git a/src/MongoDB.Bson/IO/BsonWriter.cs b/src/MongoDB.Bson/IO/BsonWriter.cs index 3fd705fa31b..1e88a64b400 100644 --- a/src/MongoDB.Bson/IO/BsonWriter.cs +++ b/src/MongoDB.Bson/IO/BsonWriter.cs @@ -326,13 +326,15 @@ public virtual void WriteRawBsonArray(IByteBuffer slice) stream.Position = 0; using (var reader = new BsonBinaryReader(stream, BsonBinaryReaderSettings.Defaults)) { - var deserializationContext = BsonDeserializationContext.CreateRoot(reader); + //QUESTION Is it correct we only need a default domain here? + var deserializationContext = BsonDeserializationContext.CreateRoot(reader, BsonSerializer.DefaultSerializationDomain); reader.ReadStartDocument(); reader.ReadName("x"); var array = BsonArraySerializer.Instance.Deserialize(deserializationContext); reader.ReadEndDocument(); - var serializationContext = BsonSerializationContext.CreateRoot(this); + //QUESTION Is it correct we only need a default domain here? + var serializationContext = BsonSerializationContext.CreateRoot(this, BsonSerializer.DefaultSerializationDomain); BsonArraySerializer.Instance.Serialize(serializationContext, array); } } @@ -350,10 +352,12 @@ public virtual void WriteRawBsonDocument(IByteBuffer slice) using (var stream = new ByteBufferStream(slice, ownsBuffer: false)) using (var bsonReader = new BsonBinaryReader(stream, BsonBinaryReaderSettings.Defaults)) { - var deserializationContext = BsonDeserializationContext.CreateRoot(bsonReader); + //QUESTION Is it correct we only need a default domain here? + var deserializationContext = BsonDeserializationContext.CreateRoot(bsonReader, BsonSerializer.DefaultSerializationDomain); var document = BsonDocumentSerializer.Instance.Deserialize(deserializationContext); - var serializationContext = BsonSerializationContext.CreateRoot(this); + //QUESTION Is it correct we only need a default domain here? + var serializationContext = BsonSerializationContext.CreateRoot(this, BsonSerializer.DefaultSerializationDomain); BsonDocumentSerializer.Instance.Serialize(serializationContext, document); } } diff --git a/src/MongoDB.Bson/IO/BsonWriterSettings.cs b/src/MongoDB.Bson/IO/BsonWriterSettings.cs index 06957993d70..82b92538605 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 { diff --git a/src/MongoDB.Bson/IO/ElementAppendingBsonWriter.cs b/src/MongoDB.Bson/IO/ElementAppendingBsonWriter.cs index 0e83adcdb91..67ff2a866c0 100644 --- a/src/MongoDB.Bson/IO/ElementAppendingBsonWriter.cs +++ b/src/MongoDB.Bson/IO/ElementAppendingBsonWriter.cs @@ -60,7 +60,8 @@ public override void WriteEndDocument() Wrapped.PushSettings(_settingsConfigurator); try { - var context = BsonSerializationContext.CreateRoot(Wrapped); + //QUESTION Is it correct we only need a default domain here? + var context = BsonSerializationContext.CreateRoot(Wrapped, BsonSerializer.DefaultSerializationDomain); foreach (var element in _elements) { Wrapped.WriteName(element.Name); diff --git a/src/MongoDB.Bson/IO/IBsonReader.cs b/src/MongoDB.Bson/IO/IBsonReader.cs index 14af4722a33..89f617fe124 100644 --- a/src/MongoDB.Bson/IO/IBsonReader.cs +++ b/src/MongoDB.Bson/IO/IBsonReader.cs @@ -17,6 +17,7 @@ namespace MongoDB.Bson.IO { + //FP This interface should have the settingg property as well, same way it's done for thr writer /// /// Represents a BSON reader. /// diff --git a/src/MongoDB.Bson/InternalExtensions.cs b/src/MongoDB.Bson/InternalExtensions.cs new file mode 100644 index 00000000000..1df207a684d --- /dev/null +++ b/src/MongoDB.Bson/InternalExtensions.cs @@ -0,0 +1,107 @@ +/* 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 MongoDB.Bson.IO; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Conventions; + +namespace MongoDB.Bson +{ + //FP This could be moved somewhere else, and maybe reordered. + internal static class InternalExtensions + { + #region IBsonIdProvider + + public static bool GetDocumentIdInternal(this IBsonIdProvider provider, object document, IBsonSerializationDomain serializationDomain, out object id, + out Type idNominalType, out IIdGenerator idGenerator) + { + if (provider is IBsonIdProviderInternal internalProvider) + { + return internalProvider.GetDocumentId(document, serializationDomain, out id, out idNominalType, out idGenerator); + } + return provider.GetDocumentId(document, out id, out idNominalType, out idGenerator); + } + + #endregion + + #region IDiscriminatorConvention + + public static Type GetActualTypeInternal(this IDiscriminatorConvention discriminatorConvention, IBsonReader bsonReader, Type nominalType, IBsonSerializationDomain serializationDomain) + { + if (discriminatorConvention is IDiscriminatorConventionInternal internalConvention) + { + return internalConvention.GetActualType(bsonReader, nominalType, serializationDomain); + } + return discriminatorConvention.GetActualType(bsonReader, nominalType); + } + + public static BsonValue GetDiscriminatorInternal(this IDiscriminatorConvention discriminatorConvention, Type nominalType, Type actualType, IBsonSerializationDomain serializationDomain) + { + if (discriminatorConvention is IDiscriminatorConventionInternal internalConvention) + { + return internalConvention.GetDiscriminator(nominalType, actualType, serializationDomain); + } + return discriminatorConvention.GetDiscriminator(nominalType, actualType); + } + + #endregion + + #region IScalarDiscriminatorConvention + + public static BsonValue[] GetDiscriminatorsForTypeAndSubTypesInternal(this IScalarDiscriminatorConvention discriminatorConvention, Type type, IBsonSerializationDomain serializationDomain) + { + if (discriminatorConvention is IScalarDiscriminatorConventionInternal internalConvention) + { + return internalConvention.GetDiscriminatorsForTypeAndSubTypes(type, serializationDomain); + } + return discriminatorConvention.GetDiscriminatorsForTypeAndSubTypes(type); + } + + #endregion + + #region IMemberMapConvention + + public static void ApplyInternal(this IMemberMapConvention memberMapConvention, BsonMemberMap memberMap, IBsonSerializationDomain serializationDomain) + { + if (memberMapConvention is IMemberMapConventionInternal internalConvention) + { + internalConvention.Apply(memberMap, serializationDomain); + } + else + { + memberMapConvention.Apply(memberMap); + } + } + + #endregion + + #region IPostProcessingConvention + + public static void PostProcessInternal(this IPostProcessingConvention postProcessingConvention, BsonClassMap classMap, IBsonSerializationDomain serializationDomain) + { + if (postProcessingConvention is IPostProcessingConventionInternal internalConvention) + { + internalConvention.PostProcess(classMap, serializationDomain); + } + else + { + postProcessingConvention.PostProcess(classMap); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/MongoDB.Bson/ObjectModel/BsonDocument.cs b/src/MongoDB.Bson/ObjectModel/BsonDocument.cs index 535bb541b62..e6a0a02d74d 100644 --- a/src/MongoDB.Bson/ObjectModel/BsonDocument.cs +++ b/src/MongoDB.Bson/ObjectModel/BsonDocument.cs @@ -331,7 +331,8 @@ public static BsonDocument Parse(string json) { using (var jsonReader = new JsonReader(json)) { - var context = BsonDeserializationContext.CreateRoot(jsonReader); + //QUESTION Is it correct we only need a default domain here? + var context = BsonDeserializationContext.CreateRoot(jsonReader, BsonSerializer.DefaultSerializationDomain); var document = BsonDocumentSerializer.Instance.Deserialize(context); if (!jsonReader.IsAtEndOfFile()) { diff --git a/src/MongoDB.Bson/ObjectModel/BsonDocumentWrapper.cs b/src/MongoDB.Bson/ObjectModel/BsonDocumentWrapper.cs index 09edc32dbfa..8965a4d0f8a 100644 --- a/src/MongoDB.Bson/ObjectModel/BsonDocumentWrapper.cs +++ b/src/MongoDB.Bson/ObjectModel/BsonDocumentWrapper.cs @@ -91,9 +91,12 @@ public object Wrapped /// The nominal type of the wrapped object. /// The wrapped object. /// A BsonDocumentWrapper. - public static BsonDocumentWrapper Create(TNominalType value) + public static BsonDocumentWrapper Create(TNominalType value) => + Create(value, BsonSerializer.DefaultSerializationDomain); + + internal static BsonDocumentWrapper Create(TNominalType value, IBsonSerializationDomain serializationDomain) { - return Create(typeof(TNominalType), value); + return Create(typeof(TNominalType), value, serializationDomain); } /// @@ -102,9 +105,12 @@ public static BsonDocumentWrapper Create(TNominalType value) /// The nominal type of the wrapped object. /// The wrapped object. /// A BsonDocumentWrapper. - public static BsonDocumentWrapper Create(Type nominalType, object value) + public static BsonDocumentWrapper Create(Type nominalType, object value) => + Create(nominalType, value, BsonSerializer.DefaultSerializationDomain); + + internal static BsonDocumentWrapper Create(Type nominalType, object value, IBsonSerializationDomain domain) { - var serializer = BsonSerializer.LookupSerializer(nominalType); + var serializer = domain.LookupSerializer(nominalType); return new BsonDocumentWrapper(value, serializer); } @@ -114,14 +120,25 @@ public static BsonDocumentWrapper Create(Type nominalType, object value) /// The nominal type of the wrapped objects. /// A list of wrapped objects. /// A list of BsonDocumentWrappers. - public static IEnumerable CreateMultiple(IEnumerable values) + public static IEnumerable CreateMultiple(IEnumerable values) => + CreateMultiple(values, BsonSerializer.DefaultSerializationDomain); + + /// + /// //TODO + /// + /// + /// + /// + /// + /// + internal static IEnumerable CreateMultiple(IEnumerable values, IBsonSerializationDomain domain) { if (values == null) { throw new ArgumentNullException("values"); } - var serializer = BsonSerializer.LookupSerializer(typeof(TNominalType)); + var serializer = domain.LookupSerializer(typeof(TNominalType)); return values.Select(v => new BsonDocumentWrapper(v, serializer)); } @@ -131,7 +148,18 @@ public static IEnumerable CreateMultiple(IEnu /// The nominal type of the wrapped object. /// A list of wrapped objects. /// A list of BsonDocumentWrappers. - public static IEnumerable CreateMultiple(Type nominalType, IEnumerable values) + public static IEnumerable CreateMultiple(Type nominalType, IEnumerable values) => + CreateMultiple(nominalType, values, BsonSerializer.DefaultSerializationDomain); + + /// + /// //TODO + /// + /// + /// + /// + /// + /// + internal static IEnumerable CreateMultiple(Type nominalType, IEnumerable values, IBsonSerializationDomain domain) { if (nominalType == null) { @@ -142,7 +170,7 @@ public static IEnumerable CreateMultiple(Type nominalType, throw new ArgumentNullException("values"); } - var serializer = BsonSerializer.LookupSerializer(nominalType); + var serializer = domain.LookupSerializer(nominalType); return values.Cast().Select(v => new BsonDocumentWrapper(v, serializer)); } @@ -178,7 +206,8 @@ protected override IEnumerable Materialize() var writerSettings = BsonDocumentWriterSettings.Defaults; using (var bsonWriter = new BsonDocumentWriter(bsonDocument, writerSettings)) { - var context = BsonSerializationContext.CreateRoot(bsonWriter); + //QUESTION Is it correct we only need a default domain here? + var context = BsonSerializationContext.CreateRoot(bsonWriter, BsonSerializer.DefaultSerializationDomain); _serializer.Serialize(context, _wrapped); } diff --git a/src/MongoDB.Bson/ObjectModel/LazyBsonArray.cs b/src/MongoDB.Bson/ObjectModel/LazyBsonArray.cs index f9232489f4f..98d44f5c5ec 100644 --- a/src/MongoDB.Bson/ObjectModel/LazyBsonArray.cs +++ b/src/MongoDB.Bson/ObjectModel/LazyBsonArray.cs @@ -173,7 +173,8 @@ private IEnumerable MaterializeThisLevel() using (var stream = new ByteBufferStream(_slice, ownsBuffer: false)) using (var bsonReader = new BsonBinaryReader(stream, _readerSettings)) { - var context = BsonDeserializationContext.CreateRoot(bsonReader); + //QUESTION Is it correct we only need a default domain here? + var context = BsonDeserializationContext.CreateRoot(bsonReader, BsonSerializer.DefaultSerializationDomain); bsonReader.ReadStartDocument(); BsonType bsonType; diff --git a/src/MongoDB.Bson/ObjectModel/LazyBsonDocument.cs b/src/MongoDB.Bson/ObjectModel/LazyBsonDocument.cs index a95e766a3b8..3e2c9d66292 100644 --- a/src/MongoDB.Bson/ObjectModel/LazyBsonDocument.cs +++ b/src/MongoDB.Bson/ObjectModel/LazyBsonDocument.cs @@ -184,7 +184,8 @@ private IEnumerable MaterializeThisLevel() using (var stream = new ByteBufferStream(_slice, ownsBuffer: false)) using (var bsonReader = new BsonBinaryReader(stream, _readerSettings)) { - var context = BsonDeserializationContext.CreateRoot(bsonReader); + //QUESTION Is it correct we only need a default domain here? + var context = BsonDeserializationContext.CreateRoot(bsonReader, BsonSerializer.DefaultSerializationDomain); bsonReader.ReadStartDocument(); BsonType bsonType; diff --git a/src/MongoDB.Bson/ObjectModel/RawBsonArray.cs b/src/MongoDB.Bson/ObjectModel/RawBsonArray.cs index 9ddbd6fcb57..2fefa970b51 100644 --- a/src/MongoDB.Bson/ObjectModel/RawBsonArray.cs +++ b/src/MongoDB.Bson/ObjectModel/RawBsonArray.cs @@ -128,7 +128,8 @@ public override IEnumerable Values using (var stream = new ByteBufferStream(_slice, ownsBuffer: false)) using (var bsonReader = new BsonBinaryReader(stream, _readerSettings)) { - var context = BsonDeserializationContext.CreateRoot(bsonReader); + //QUESTION Is it correct we only need a default domain here? + var context = BsonDeserializationContext.CreateRoot(bsonReader, BsonSerializer.DefaultSerializationDomain); bsonReader.ReadStartDocument(); while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) @@ -167,7 +168,8 @@ public override BsonValue this[int index] bsonReader.SkipName(); if (i == index) { - var context = BsonDeserializationContext.CreateRoot(bsonReader); + //QUESTION Is it correct we only need a default domain here? + var context = BsonDeserializationContext.CreateRoot(bsonReader, BsonSerializer.DefaultSerializationDomain); return DeserializeBsonValue(context); } @@ -314,7 +316,8 @@ public override bool Contains(BsonValue value) using (var stream = new ByteBufferStream(_slice, ownsBuffer: false)) using (var bsonReader = new BsonBinaryReader(stream, _readerSettings)) { - var context = BsonDeserializationContext.CreateRoot(bsonReader); + //QUESTION Is it correct we only need a default domain here? + var context = BsonDeserializationContext.CreateRoot(bsonReader, BsonSerializer.DefaultSerializationDomain); bsonReader.ReadStartDocument(); while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) @@ -342,7 +345,8 @@ public override void CopyTo(BsonValue[] array, int arrayIndex) using (var stream = new ByteBufferStream(_slice, ownsBuffer: false)) using (var bsonReader = new BsonBinaryReader(stream, _readerSettings)) { - var context = BsonDeserializationContext.CreateRoot(bsonReader); + //QUESTION Is it correct we only need a default domain here? + var context = BsonDeserializationContext.CreateRoot(bsonReader, BsonSerializer.DefaultSerializationDomain); bsonReader.ReadStartDocument(); while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) @@ -383,7 +387,8 @@ public override IEnumerator GetEnumerator() using (var stream = new ByteBufferStream(_slice, ownsBuffer: false)) using (var bsonReader = new BsonBinaryReader(stream, _readerSettings)) { - var context = BsonDeserializationContext.CreateRoot(bsonReader); + //QUESTION Is it correct we only need a default domain here? + var context = BsonDeserializationContext.CreateRoot(bsonReader, BsonSerializer.DefaultSerializationDomain); bsonReader.ReadStartDocument(); while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) @@ -429,7 +434,8 @@ public override int IndexOf(BsonValue value, int index, int count) using (var stream = new ByteBufferStream(_slice, ownsBuffer: false)) using (var bsonReader = new BsonBinaryReader(stream, _readerSettings)) { - var context = BsonDeserializationContext.CreateRoot(bsonReader); + //QUESTION Is it correct we only need a default domain here? + var context = BsonDeserializationContext.CreateRoot(bsonReader, BsonSerializer.DefaultSerializationDomain); bsonReader.ReadStartDocument(); var i = 0; @@ -489,7 +495,8 @@ public BsonArray Materialize(BsonBinaryReaderSettings binaryReaderSettings) using (var stream = new MemoryStream(bytes)) using (var reader = new BsonBinaryReader(stream, binaryReaderSettings)) { - var context = BsonDeserializationContext.CreateRoot(reader); + //QUESTION Is it correct we only need a default domain here? + var context = BsonDeserializationContext.CreateRoot(reader, BsonSerializer.DefaultSerializationDomain); var materializedDocument = BsonDocumentSerializer.Instance.Deserialize(context); return materializedDocument["array"].AsBsonArray; } diff --git a/src/MongoDB.Bson/ObjectModel/RawBsonDocument.cs b/src/MongoDB.Bson/ObjectModel/RawBsonDocument.cs index 6fde27bf5c3..b58265261f2 100644 --- a/src/MongoDB.Bson/ObjectModel/RawBsonDocument.cs +++ b/src/MongoDB.Bson/ObjectModel/RawBsonDocument.cs @@ -102,7 +102,8 @@ public override IEnumerable Elements using (var stream = new ByteBufferStream(_slice, ownsBuffer: false)) using (var bsonReader = new BsonBinaryReader(stream, _readerSettings)) { - var context = BsonDeserializationContext.CreateRoot(bsonReader); + //QUESTION Is it correct we only need a default domain here? + var context = BsonDeserializationContext.CreateRoot(bsonReader, BsonSerializer.DefaultSerializationDomain); bsonReader.ReadStartDocument(); while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) @@ -164,7 +165,8 @@ public override IEnumerable Values using (var stream = new ByteBufferStream(_slice, ownsBuffer: false)) using (var bsonReader = new BsonBinaryReader(stream, _readerSettings)) { - var context = BsonDeserializationContext.CreateRoot(bsonReader); + //QUESTION Is it correct we only need a default domain here? + var context = BsonDeserializationContext.CreateRoot(bsonReader, BsonSerializer.DefaultSerializationDomain); bsonReader.ReadStartDocument(); while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) @@ -352,7 +354,8 @@ public override bool ContainsValue(BsonValue value) using (var stream = new ByteBufferStream(_slice, ownsBuffer: false)) using (var bsonReader = new BsonBinaryReader(stream, _readerSettings)) { - var context = BsonDeserializationContext.CreateRoot(bsonReader); + //QUESTION Is it correct we only need a default domain here? + var context = BsonDeserializationContext.CreateRoot(bsonReader, BsonSerializer.DefaultSerializationDomain); bsonReader.ReadStartDocument(); while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) @@ -408,7 +411,8 @@ public override BsonElement GetElement(int index) using (var stream = new ByteBufferStream(_slice, ownsBuffer: false)) using (var bsonReader = new BsonBinaryReader(stream, _readerSettings)) { - var context = BsonDeserializationContext.CreateRoot(bsonReader); + //QUESTION Is it correct we only need a default domain here? + var context = BsonDeserializationContext.CreateRoot(bsonReader, BsonSerializer.DefaultSerializationDomain); bsonReader.ReadStartDocument(); var i = 0; @@ -463,7 +467,8 @@ public override IEnumerator GetEnumerator() using (var stream = new ByteBufferStream(_slice, ownsBuffer: false)) using (var bsonReader = new BsonBinaryReader(stream, _readerSettings)) { - var context = BsonDeserializationContext.CreateRoot(bsonReader); + //QUESTION Is it correct we only need a default domain here? + var context = BsonDeserializationContext.CreateRoot(bsonReader, BsonSerializer.DefaultSerializationDomain); bsonReader.ReadStartDocument(); while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) @@ -494,7 +499,8 @@ public override BsonValue GetValue(int index) using (var stream = new ByteBufferStream(_slice, ownsBuffer: false)) using (var bsonReader = new BsonBinaryReader(stream, _readerSettings)) { - var context = BsonDeserializationContext.CreateRoot(bsonReader); + //QUESTION Is it correct we only need a default domain here? + var context = BsonDeserializationContext.CreateRoot(bsonReader, BsonSerializer.DefaultSerializationDomain); bsonReader.ReadStartDocument(); var i = 0; @@ -576,7 +582,8 @@ public BsonDocument Materialize(BsonBinaryReaderSettings binaryReaderSettings) using (var stream = new ByteBufferStream(_slice, ownsBuffer: false)) using (var reader = new BsonBinaryReader(stream, binaryReaderSettings)) { - var context = BsonDeserializationContext.CreateRoot(reader); + //QUESTION Is it correct we only need a default domain here? + var context = BsonDeserializationContext.CreateRoot(reader, BsonSerializer.DefaultSerializationDomain); return BsonDocumentSerializer.Instance.Deserialize(context); } } @@ -699,7 +706,8 @@ public override bool TryGetElement(string name, out BsonElement element) using (var stream = new ByteBufferStream(_slice, ownsBuffer: false)) using (var bsonReader = new BsonBinaryReader(stream, _readerSettings)) { - var context = BsonDeserializationContext.CreateRoot(bsonReader); + //QUESTION Is it correct we only need a default domain here? + var context = BsonDeserializationContext.CreateRoot(bsonReader, BsonSerializer.DefaultSerializationDomain); bsonReader.ReadStartDocument(); while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) @@ -734,7 +742,8 @@ public override bool TryGetValue(string name, out BsonValue value) using (var stream = new ByteBufferStream(_slice, ownsBuffer: false)) using (var bsonReader = new BsonBinaryReader(stream, _readerSettings)) { - var context = BsonDeserializationContext.CreateRoot(bsonReader); + //QUESTION Is it correct we only need a default domain here? + var context = BsonDeserializationContext.CreateRoot(bsonReader, BsonSerializer.DefaultSerializationDomain); bsonReader.ReadStartDocument(); while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) diff --git a/src/MongoDB.Bson/Properties/AssemblyInfo.cs b/src/MongoDB.Bson/Properties/AssemblyInfo.cs index ee574295e6c..2a1d28a924a 100644 --- a/src/MongoDB.Bson/Properties/AssemblyInfo.cs +++ b/src/MongoDB.Bson/Properties/AssemblyInfo.cs @@ -28,3 +28,8 @@ [assembly: InternalsVisibleTo("MongoDB.Bson.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] [assembly: InternalsVisibleTo("MongoDB.Analyzer.MQLGenerator, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] +[assembly: InternalsVisibleTo("MongoDB.Driver, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] +[assembly: InternalsVisibleTo("MongoDB.Driver.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] +[assembly: InternalsVisibleTo("MongoDB.Driver.Encryption, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] +[assembly: InternalsVisibleTo("MongoDB.Driver.Authentication.AWS, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] +[assembly: InternalsVisibleTo("MongoDB.Driver.TestHelpers, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")] diff --git a/src/MongoDB.Bson/Serialization/AttributedSerializationProvider.cs b/src/MongoDB.Bson/Serialization/AttributedSerializationProvider.cs index bc9258cb9b4..df24ee63f2c 100644 --- a/src/MongoDB.Bson/Serialization/AttributedSerializationProvider.cs +++ b/src/MongoDB.Bson/Serialization/AttributedSerializationProvider.cs @@ -20,6 +20,7 @@ namespace MongoDB.Bson.Serialization { + //DOMAIN-API We should consider making all our serialization provider classes sealed or internal. /// /// Provides serializers based on an attribute. /// diff --git a/src/MongoDB.Bson/Serialization/BsonClassMap.cs b/src/MongoDB.Bson/Serialization/BsonClassMap.cs index 0f524fe2326..1d965e89cff 100644 --- a/src/MongoDB.Bson/Serialization/BsonClassMap.cs +++ b/src/MongoDB.Bson/Serialization/BsonClassMap.cs @@ -31,15 +31,9 @@ namespace MongoDB.Bson.Serialization /// public class BsonClassMap { - // private static fields - private readonly static Dictionary __classMaps = new Dictionary(); - private readonly static Queue __knownTypesQueue = new Queue(); - private static int __freezeNestingLevel = 0; - // private fields private readonly Type _classType; private readonly List _creatorMaps; - private readonly IConventionPack _conventionPack; private readonly bool _isAnonymous; private readonly List _allMemberMaps; // includes inherited member maps private readonly ReadOnlyCollection _allMemberMapsReadonly; @@ -70,7 +64,6 @@ public BsonClassMap(Type classType) { _classType = classType; _creatorMaps = new List(); - _conventionPack = ConventionRegistry.Lookup(classType); _isAnonymous = classType.IsAnonymousType(); _allMemberMaps = new List(); _allMemberMapsReadonly = _allMemberMaps.AsReadOnly(); @@ -124,12 +117,13 @@ public IEnumerable CreatorMaps get { return _creatorMaps; } } + //DOMAIN-API This one should be removed, or become a method to get the convention registry/domain as input /// /// Gets the conventions used for auto mapping. /// public IConventionPack ConventionPack { - get { return _conventionPack; } + get { return BsonSerializer.DefaultSerializationDomain.ConventionRegistry.Lookup(_classType); } } /// @@ -253,6 +247,7 @@ internal int ExtraElementsMemberMapIndex get { return _extraElementsMemberIndex; } } + //DOMAIN-API This is a utility method, it should not be public. // public static methods /// /// Gets the type of a member. @@ -282,103 +277,32 @@ public static Type GetMemberInfoType(MemberInfo memberInfo) /// 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() => + BsonSerializer.DefaultSerializationDomain.BsonClassMap.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) => + BsonSerializer.DefaultSerializationDomain.BsonClassMap.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) => + BsonSerializer.DefaultSerializationDomain.BsonClassMap.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()=> + BsonSerializer.DefaultSerializationDomain.BsonClassMap.RegisterClassMap(); /// /// Creates and registers a class map. @@ -387,35 +311,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; - } + => BsonSerializer.DefaultSerializationDomain.BsonClassMap.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(); - } - } + => BsonSerializer.DefaultSerializationDomain.BsonClassMap.RegisterClassMap(classMap); /// /// Registers a class map if it is not already registered. @@ -423,16 +326,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; - } - } + => BsonSerializer.DefaultSerializationDomain.BsonClassMap.TryRegisterClassMap(); /// /// Registers a class map if it is not already registered. @@ -441,19 +335,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; - } - } + => BsonSerializer.DefaultSerializationDomain.BsonClassMap.TryRegisterClassMap(classMap); /// /// Registers a class map if it is not already registered. @@ -462,19 +344,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); - } - } + => BsonSerializer.DefaultSerializationDomain.BsonClassMap.TryRegisterClassMap(classMapInitializer); /// /// Registers a class map if it is not already registered. @@ -483,54 +353,22 @@ 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(); - } - } + => BsonSerializer.DefaultSerializationDomain.BsonClassMap.TryRegisterClassMap(classMapFactory); // public methods /// /// Automaps the class. /// - public void AutoMap() + public void AutoMap() => AutoMap(BsonSerializer.DefaultSerializationDomain); + + /// + /// //TODO + /// + /// + internal void AutoMap(IBsonSerializationDomain serializationDomain) { if (_frozen) { ThrowFrozenException(); } - AutoMapClass(); + AutoMapClass(serializationDomain); } /// @@ -573,13 +411,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(BsonSerializer.DefaultSerializationDomain); + + internal BsonClassMap Freeze(IBsonSerializationDomain domain) + { + var freezeContext = new FreezeContext { SerializationDomain = domain }; + return Freeze(freezeContext); + } + + private BsonClassMap Freeze(FreezeContext context) { - BsonSerializer.ConfigLock.EnterReadLock(); + var configLock = context.SerializationDomain!.ConfigLock; + configLock.EnterReadLock(); try { if (_frozen) @@ -589,15 +443,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 +459,9 @@ public BsonClassMap Freeze() { if (_baseClassMap == null) { - _baseClassMap = LookupClassMap(baseType); + _baseClassMap = context.SerializationDomain.BsonClassMap.LookupClassMap(baseType); } - _baseClassMap.Freeze(); + _baseClassMap.Freeze(context); _discriminatorIsRequired |= _baseClassMap._discriminatorIsRequired; _hasRootClass |= (_isRootClass || _baseClassMap.HasRootClass); _allMemberMaps.AddRange(_baseClassMap.AllMemberMaps); @@ -699,28 +553,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.BsonClassMap.LookupClassMap(knownType); // will AutoMap and/or Freeze knownType if necessary } } } finally { - __freezeNestingLevel--; + context.FreezeNestingLevel--; } } } finally { - BsonSerializer.ConfigLock.ExitWriteLock(); + configLock.ExitWriteLock(); } return this; } @@ -1317,7 +1171,7 @@ public void UnmapProperty(string propertyName) /// Gets the discriminator convention for the class. /// /// The discriminator convention for the class. - internal IDiscriminatorConvention GetDiscriminatorConvention() + internal IDiscriminatorConvention GetDiscriminatorConvention(IBsonSerializationDomain serializationDomain) { // return a cached discriminator convention when possible var discriminatorConvention = _discriminatorConvention; @@ -1354,7 +1208,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 +1217,29 @@ 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); + serializationDomain.GetOrRegisterDiscriminatorConvention(classMap._classType, StandardDiscriminatorConvention.Hierarchical); break; } classMap = classMap._baseClassMap; } - return BsonSerializer.LookupDiscriminatorConvention(_classType); + return serializationDomain.LookupDiscriminatorConvention(_classType); } } + /// + /// Gets the discriminator convention for the class. + /// + /// The discriminator convention for the class. + internal IDiscriminatorConvention GetDiscriminatorConvention() + => GetDiscriminatorConvention(BsonSerializer.DefaultSerializationDomain); + // private methods - private void AutoMapClass() + private void AutoMapClass(IBsonSerializationDomain serializationDomain) { - new ConventionRunner(_conventionPack).Apply(this); + var conventionPack = serializationDomain.ConventionRegistry.Lookup(_classType); + new ConventionRunner(conventionPack).Apply(this, serializationDomain); foreach (var memberMap in _declaredMemberMaps) { diff --git a/src/MongoDB.Bson/Serialization/BsonClassMapDomain.cs b/src/MongoDB.Bson/Serialization/BsonClassMapDomain.cs new file mode 100644 index 00000000000..9efe6c84a1d --- /dev/null +++ b/src/MongoDB.Bson/Serialization/BsonClassMapDomain.cs @@ -0,0 +1,263 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace MongoDB.Bson.Serialization; + +internal class BsonClassMapDomain : IBsonClassMapDomain +{ + // private fields + private readonly IBsonSerializationDomain _serializationDomain; + private readonly Dictionary _classMaps = new(); + + public BsonClassMapDomain(BsonSerializationDomain serializationDomain) + { + _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 newClassMap = (BsonClassMap)Activator.CreateInstance(classMapType); + newClassMap.AutoMap(_serializationDomain); + + _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(_serializationDomain); }); + } + + /// + /// Creates and registers a class map. + /// + /// The class. + /// The class map initializer. + /// The class map. + public BsonClassMap RegisterClassMap(Action> classMapInitializer) + { + var classMap = new BsonClassMap(classMapInitializer); + RegisterClassMap(classMap); + return classMap; + } + + /// + /// Registers a class map. + /// + /// The class map. + public void RegisterClassMap(BsonClassMap classMap) + { + if (classMap == null) + { + throw new ArgumentNullException("classMap"); + } + + _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(_serializationDomain)); + + static BsonClassMap ClassMapFactory(IBsonSerializationDomain serializationDomain) + { + var classMap = new BsonClassMap(); + classMap.AutoMap(serializationDomain); + 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(); + } + } +} \ No newline at end of file diff --git a/src/MongoDB.Bson/Serialization/BsonClassMapSerializationProvider.cs b/src/MongoDB.Bson/Serialization/BsonClassMapSerializationProvider.cs index ac088360c2d..563c0871780 100644 --- a/src/MongoDB.Bson/Serialization/BsonClassMapSerializationProvider.cs +++ b/src/MongoDB.Bson/Serialization/BsonClassMapSerializationProvider.cs @@ -21,10 +21,21 @@ namespace MongoDB.Bson.Serialization /// /// Represents the class map serialization provider. /// - internal class BsonClassMapSerializationProvider : BsonSerializationProviderBase + internal class BsonClassMapSerializationProvider : BsonSerializationProviderBase, IDomainAwareBsonSerializationProvider { + public IBsonSerializationDomain SerializationDomain { get; } + + public BsonClassMapSerializationProvider(IBsonSerializationDomain serializationDomain) + { + SerializationDomain = serializationDomain; + } + + //DOMAIN-API This method should be removed. /// public override IBsonSerializer GetSerializer(Type type, IBsonSerializerRegistry serializerRegistry) + => GetSerializerWithDomain(type); + + public IBsonSerializer GetSerializerWithDomain(Type type) { if (type == null) { @@ -41,7 +52,7 @@ 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.BsonClassMap.LookupClassMap(type); var classMapSerializerDefinition = typeof(BsonClassMapSerializer<>); var classMapSerializerType = classMapSerializerDefinition.MakeGenericType(type); return (IBsonSerializer)Activator.CreateInstance(classMapSerializerType, classMap); diff --git a/src/MongoDB.Bson/Serialization/BsonDeserializationContext.cs b/src/MongoDB.Bson/Serialization/BsonDeserializationContext.cs index 03ab3137326..696e2f9c39b 100644 --- a/src/MongoDB.Bson/Serialization/BsonDeserializationContext.cs +++ b/src/MongoDB.Bson/Serialization/BsonDeserializationContext.cs @@ -14,6 +14,7 @@ */ using System; +using System.Net; using MongoDB.Bson.IO; namespace MongoDB.Bson.Serialization @@ -28,10 +29,12 @@ public class BsonDeserializationContext private readonly IBsonSerializer _dynamicArraySerializer; private readonly IBsonSerializer _dynamicDocumentSerializer; private readonly IBsonReader _reader; + private readonly IBsonSerializationDomain _serializationDomain; // constructors private BsonDeserializationContext( IBsonReader reader, + IBsonSerializationDomain serializationDomain, bool allowDuplicateElementNames, IBsonSerializer dynamicArraySerializer, IBsonSerializer dynamicDocumentSerializer) @@ -40,6 +43,12 @@ private BsonDeserializationContext( _allowDuplicateElementNames = allowDuplicateElementNames; _dynamicArraySerializer = dynamicArraySerializer; _dynamicDocumentSerializer = dynamicDocumentSerializer; + + _serializationDomain = serializationDomain; //FP Using this version to find error in an easier way for now + //_serializationDomain = serializationDomain ?? BsonSerializer.DefaultSerializationDomain; + + _dynamicArraySerializer ??= _serializationDomain.BsonDefaults.DynamicArraySerializer; + _dynamicDocumentSerializer ??= _serializationDomain.BsonDefaults.DynamicDocumentSerializer; } // public properties @@ -54,6 +63,11 @@ public bool AllowDuplicateElementNames get { return _allowDuplicateElementNames; } } + /// + /// //TODO + /// + internal IBsonSerializationDomain SerializationDomain => _serializationDomain; + /// /// Gets the dynamic array serializer. /// @@ -87,6 +101,7 @@ public IBsonReader Reader get { return _reader; } } + // //DOMAIN-API We should remove this version of the CreateRoot method, and use the one that takes a serialization domain. // public static methods /// /// Creates a root context. @@ -99,8 +114,14 @@ public IBsonReader Reader public static BsonDeserializationContext CreateRoot( IBsonReader reader, Action configurator = null) + => CreateRoot(reader, BsonSerializer.DefaultSerializationDomain, configurator); + + internal static BsonDeserializationContext CreateRoot( + IBsonReader reader, + IBsonSerializationDomain serializationDomain, + Action configurator = null) { - var builder = new Builder(null, reader); + var builder = new Builder(null, reader, serializationDomain); if (configurator != null) { configurator(builder); @@ -119,7 +140,7 @@ public static BsonDeserializationContext CreateRoot( public BsonDeserializationContext With( Action configurator = null) { - var builder = new Builder(this, _reader); + var builder = new Builder(this, _reader, _serializationDomain); if (configurator != null) { configurator(builder); @@ -138,9 +159,10 @@ public class Builder private IBsonSerializer _dynamicArraySerializer; private IBsonSerializer _dynamicDocumentSerializer; private IBsonReader _reader; + private IBsonSerializationDomain _serializationDomain; // constructors - internal Builder(BsonDeserializationContext other, IBsonReader reader) + internal Builder(BsonDeserializationContext other, IBsonReader reader, IBsonSerializationDomain serializationDomain) { if (reader == null) { @@ -148,17 +170,17 @@ internal Builder(BsonDeserializationContext other, IBsonReader reader) } _reader = reader; + _serializationDomain = serializationDomain; if (other != null) { _allowDuplicateElementNames = other.AllowDuplicateElementNames; _dynamicArraySerializer = other.DynamicArraySerializer; _dynamicDocumentSerializer = other.DynamicDocumentSerializer; } - else - { - _dynamicArraySerializer = BsonDefaults.DynamicArraySerializer; - _dynamicDocumentSerializer = BsonDefaults.DynamicDocumentSerializer; - } + + /* QUESTION I removed the part where we set the dynamic serializers from the BsonDefaults, and delay it until we have a serialization domain (when we build the DeserializationContext). + * This is technically changing the public behaviour, but it's in a builder, I do not thing it will affect anyone. Same done for the serialization context. + */ } // properties @@ -209,6 +231,8 @@ public IBsonReader Reader get { return _reader; } } + internal IBsonSerializationDomain SerializationDomain => _serializationDomain; + // public methods /// /// Builds the BsonDeserializationContext instance. @@ -216,7 +240,7 @@ public IBsonReader Reader /// A BsonDeserializationContext. internal BsonDeserializationContext Build() { - return new BsonDeserializationContext(_reader, _allowDuplicateElementNames, _dynamicArraySerializer, _dynamicDocumentSerializer); + return new BsonDeserializationContext(_reader, _serializationDomain, _allowDuplicateElementNames, _dynamicArraySerializer, _dynamicDocumentSerializer); } } } diff --git a/src/MongoDB.Bson/Serialization/BsonMemberMap.cs b/src/MongoDB.Bson/Serialization/BsonMemberMap.cs index b10971d5249..1181af3c7a8 100644 --- a/src/MongoDB.Bson/Serialization/BsonMemberMap.cs +++ b/src/MongoDB.Bson/Serialization/BsonMemberMap.cs @@ -56,7 +56,7 @@ public BsonMemberMap(BsonClassMap classMap, MemberInfo memberInfo) { _classMap = classMap; _memberInfo = memberInfo; - _memberType = BsonClassMap.GetMemberInfoType(memberInfo); + _memberType = BsonClassMap.GetMemberInfoType(memberInfo); //FP This is more of a utility method, it can stay like this _memberTypeIsBsonValue = typeof(BsonValue).GetTypeInfo().IsAssignableFrom(_memberType); Reset(); @@ -294,14 +294,16 @@ obj is BsonMemberMap other && /// Gets the serializer. /// /// The serializer. - public IBsonSerializer GetSerializer() + public IBsonSerializer GetSerializer() => GetSerializer(BsonSerializer.DefaultSerializationDomain); + + internal IBsonSerializer GetSerializer(IBsonSerializationDomain domain) { if (_serializer == null) { // return special serializer for BsonValue members that handles the _csharpnull representation if (_memberTypeIsBsonValue) { - var wrappedSerializer = BsonSerializer.LookupSerializer(_memberType); + var wrappedSerializer = domain.LookupSerializer(_memberType); var isBsonArraySerializer = wrappedSerializer is IBsonArraySerializer; var isBsonDocumentSerializer = wrappedSerializer is IBsonDocumentSerializer; @@ -329,7 +331,7 @@ public IBsonSerializer GetSerializer() } else { - _serializer = BsonSerializer.LookupSerializer(_memberType); + _serializer = domain.LookupSerializer(_memberType); } } return _serializer; diff --git a/src/MongoDB.Bson/Serialization/BsonSerializationContext.cs b/src/MongoDB.Bson/Serialization/BsonSerializationContext.cs index a14a62322a8..8c046d91911 100644 --- a/src/MongoDB.Bson/Serialization/BsonSerializationContext.cs +++ b/src/MongoDB.Bson/Serialization/BsonSerializationContext.cs @@ -26,17 +26,31 @@ public class BsonSerializationContext // private fields private readonly Func _isDynamicType; private readonly IBsonWriter _writer; + private readonly IBsonSerializationDomain _serializationDomain; // constructors private BsonSerializationContext( IBsonWriter writer, + IBsonSerializationDomain serializationDomain, Func isDynamicType) { _writer = writer; _isDynamicType = isDynamicType; + + _serializationDomain = serializationDomain; //FP Using this version to find error in an easier way for now + //_serializationDomain = serializationDomain ?? BsonSerializer.DefaultSerializationDomain; + + _isDynamicType??= t => + (_serializationDomain.BsonDefaults.DynamicArraySerializer != null && t == _serializationDomain.BsonDefaults.DynamicArraySerializer.ValueType) || + (_serializationDomain.BsonDefaults.DynamicDocumentSerializer != null && t == _serializationDomain.BsonDefaults.DynamicDocumentSerializer.ValueType); } // public properties + /// + /// //TODO + /// + internal IBsonSerializationDomain SerializationDomain => _serializationDomain; + /// /// Gets a function that, when executed, will indicate whether the type /// is a dynamic type. @@ -57,6 +71,7 @@ public IBsonWriter Writer get { return _writer; } } + //DOMAIN-API This method should be probably removed. // public static methods /// /// Creates a root context. @@ -69,8 +84,14 @@ public IBsonWriter Writer public static BsonSerializationContext CreateRoot( IBsonWriter writer, Action configurator = null) + => CreateRoot(writer, BsonSerializer.DefaultSerializationDomain, configurator); + + internal static BsonSerializationContext CreateRoot( + IBsonWriter writer, + IBsonSerializationDomain serializationDomain, + Action configurator = null) { - var builder = new Builder(null, writer); + var builder = new Builder(null, writer, serializationDomain); if (configurator != null) { configurator(builder); @@ -88,7 +109,7 @@ public static BsonSerializationContext CreateRoot( public BsonSerializationContext With( Action configurator = null) { - var builder = new Builder(this, _writer); + var builder = new Builder(this, _writer, _serializationDomain); if (configurator != null) { configurator(builder); @@ -105,9 +126,10 @@ public class Builder // private fields private Func _isDynamicType; private IBsonWriter _writer; + private IBsonSerializationDomain _serializationDomain; // constructors - internal Builder(BsonSerializationContext other, IBsonWriter writer) + internal Builder(BsonSerializationContext other, IBsonWriter writer, IBsonSerializationDomain serializationDomain) { if (writer == null) { @@ -115,16 +137,15 @@ internal Builder(BsonSerializationContext other, IBsonWriter writer) } _writer = writer; + _serializationDomain = serializationDomain; if (other != null) { _isDynamicType = other._isDynamicType; } - else - { - _isDynamicType = t => - (BsonDefaults.DynamicArraySerializer != null && t == BsonDefaults.DynamicArraySerializer.ValueType) || - (BsonDefaults.DynamicDocumentSerializer != null && t == BsonDefaults.DynamicDocumentSerializer.ValueType); - } + + /* QUESTION I removed the part where we set _isDynamicType from the BsonDefaults, and delay it until we have a serialization domain (when we build the SerializationContext). + * This is technically changing the public behaviour, but it's in a builder, I do not thing it will affect anyone. Same done for the deserialization context. + */ } // properties @@ -155,7 +176,7 @@ public IBsonWriter Writer /// A BsonSerializationContext. internal BsonSerializationContext Build() { - return new BsonSerializationContext(_writer, _isDynamicType); + return new BsonSerializationContext(_writer, _serializationDomain, _isDynamicType); } } } diff --git a/src/MongoDB.Bson/Serialization/BsonSerializationDomain.cs b/src/MongoDB.Bson/Serialization/BsonSerializationDomain.cs new file mode 100644 index 00000000000..8ffebd8b08d --- /dev/null +++ b/src/MongoDB.Bson/Serialization/BsonSerializationDomain.cs @@ -0,0 +1,878 @@ +/* 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.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading; +using MongoDB.Bson.IO; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Bson.Serialization.Conventions; +using MongoDB.Bson.Serialization.IdGenerators; + +namespace MongoDB.Bson.Serialization +{ + /// + /// A class that represents the BSON serialization functionality. + /// + internal class BsonSerializationDomain : IBsonSerializationDomain, IDisposable + { + // private fields + private IBsonDefaults _bsonDefaults; + private ReaderWriterLockSlim _configLock = new(LockRecursionPolicy.SupportsRecursion); + private IBsonClassMapDomain _classMapDomain; + private IConventionRegistryDomain _conventionRegistryDomain; + private Dictionary _idGenerators = new(); + private Dictionary _discriminatorConventions = new(); + private Dictionary> _discriminators = new(); + private HashSet _discriminatedTypes = new(); + private BsonSerializerRegistry _serializerRegistry; + private TypeMappingSerializationProvider _typeMappingSerializationProvider; + // ConcurrentDictionary is being used as a concurrent set of Type. The values will always be null. + private ConcurrentDictionary _typesWithRegisteredKnownTypes = new(); + + private bool _useNullIdChecker; + private bool _useZeroIdChecker; + + // constructor + public BsonSerializationDomain(string name = null) + { + CreateSerializerRegistry(); + CreateSubDomains(); + RegisterIdGenerators(); + Name = name ?? "CUSTOM"; + } + + public string Name { get; } + + // public properties + /// + /// Gets the serializer registry. + /// + public IBsonSerializerRegistry SerializerRegistry + { + get { return _serializerRegistry; } + } + + /// + /// Gets or sets whether to use the NullIdChecker on reference Id types that don't have an IdGenerator registered. + /// + public bool UseNullIdChecker + { + get { return _useNullIdChecker; } + set { _useNullIdChecker = value; } + } + + public bool UseNullIdCheckerEnabled => UseNullIdChecker; + + /// + /// Gets or sets whether to use the ZeroIdChecker on value Id types that don't have an IdGenerator registered. + /// + public bool UseZeroIdChecker + { + get { return _useZeroIdChecker; } + set { _useZeroIdChecker = value; } + } + + public bool UseZeroIdCheckerEnabled => UseZeroIdChecker; + + // internal properties + public ReaderWriterLockSlim ConfigLock + { + get { return _configLock; } + } + + /// + /// Deserializes an object from a BsonDocument. + /// + /// The nominal type of the object. + /// The BsonDocument. + /// The configurator. + /// A deserialized value. + public TNominalType Deserialize(BsonDocument document, + Action configurator = null) + { + using (var bsonReader = new BsonDocumentReader(document)) + { + return Deserialize(bsonReader, configurator); + } + } + + /// + /// Deserializes a value. + /// + /// The nominal type of the object. + /// The BsonReader. + /// The configurator. + /// A deserialized value. + public TNominalType Deserialize(IBsonReader bsonReader, + Action configurator = null) + { + var serializer = LookupSerializer(); + var context = BsonDeserializationContext.CreateRoot(bsonReader, this, configurator); + return serializer.Deserialize(context); + } + + /// + /// Deserializes an object from a BSON byte array. + /// + /// The nominal type of the object. + /// The BSON byte array. + /// The configurator. + /// A deserialized value. + public TNominalType Deserialize(byte[] bytes, + Action configurator = null) + { + using (var buffer = new ByteArrayBuffer(bytes, isReadOnly: true)) + using (var stream = new ByteBufferStream(buffer)) + { + return Deserialize(stream, configurator); + } + } + + /// + /// Deserializes an object from a BSON Stream. + /// + /// The nominal type of the object. + /// The BSON Stream. + /// The configurator. + /// A deserialized value. + public TNominalType Deserialize(Stream stream, + Action configurator = null) + { + using (var bsonReader = new BsonBinaryReader(stream)) + { + return Deserialize(bsonReader, configurator); + } + } + + /// + /// Deserializes an object from a JSON string. + /// + /// The nominal type of the object. + /// The JSON string. + /// The configurator. + /// A deserialized value. + public TNominalType Deserialize(string json, + Action configurator = null) + { + using (var bsonReader = new JsonReader(json)) + { + return Deserialize(bsonReader, configurator); + } + } + + /// + /// Deserializes an object from a JSON TextReader. + /// + /// The nominal type of the object. + /// The JSON TextReader. + /// The configurator. + /// A deserialized value. + public TNominalType Deserialize(TextReader textReader, + Action configurator = null) + { + using (var bsonReader = new JsonReader(textReader)) + { + return Deserialize(bsonReader, configurator); + } + } + + /// + /// Deserializes an object from a BsonDocument. + /// + /// The BsonDocument. + /// The nominal type of the object. + /// The configurator. + /// A deserialized value. + public object Deserialize(BsonDocument document, Type nominalType, + Action configurator = null) + { + using (var bsonReader = new BsonDocumentReader(document)) + { + return Deserialize(bsonReader, nominalType, configurator); + } + } + + /// + /// Deserializes a value. + /// + /// The BsonReader. + /// The nominal type of the object. + /// The configurator. + /// A deserialized value. + public object Deserialize(IBsonReader bsonReader, Type nominalType, + Action configurator = null) + { + var serializer = LookupSerializer(nominalType); + var context = BsonDeserializationContext.CreateRoot(bsonReader, this, configurator); + return serializer.Deserialize(context); + } + + /// + /// Deserializes an object from a BSON byte array. + /// + /// The BSON byte array. + /// The nominal type of the object. + /// The configurator. + /// A deserialized value. + public object Deserialize(byte[] bytes, Type nominalType, + Action configurator = null) + { + using (var buffer = new ByteArrayBuffer(bytes, isReadOnly: true)) + using (var stream = new ByteBufferStream(buffer)) + { + return Deserialize(stream, nominalType, configurator); + } + } + + /// + /// Deserializes an object from a BSON Stream. + /// + /// The BSON Stream. + /// The nominal type of the object. + /// The configurator. + /// A deserialized value. + public object Deserialize(Stream stream, Type nominalType, + Action configurator = null) + { + using (var bsonReader = new BsonBinaryReader(stream)) + { + return Deserialize(bsonReader, nominalType, configurator); + } + } + + /// + /// Deserializes an object from a JSON string. + /// + /// The JSON string. + /// The nominal type of the object. + /// The configurator. + /// A deserialized value. + public object Deserialize(string json, Type nominalType, + Action configurator = null) + { + using (var bsonReader = new JsonReader(json)) + { + return Deserialize(bsonReader, nominalType, configurator); + } + } + + /// + /// Deserializes an object from a JSON TextReader. + /// + /// The JSON TextReader. + /// The nominal type of the object. + /// The configurator. + /// A deserialized value. + public object Deserialize(TextReader textReader, Type nominalType, + Action configurator = null) + { + using (var bsonReader = new JsonReader(textReader)) + { + return Deserialize(bsonReader, nominalType, configurator); + } + } + + public BsonValue[] GetDiscriminatorsForTypeAndSubTypes(Type type) + { + // note: EnsureKnownTypesAreRegistered handles its own locking so call from outside any lock + EnsureKnownTypesAreRegistered(type); + + var discriminators = new List(); + + _configLock.EnterReadLock(); + try + { + foreach (var entry in _discriminators) + { + var discriminator = entry.Key; + var actualTypes = entry.Value; + + var matchingType = actualTypes.SingleOrDefault(t => t == type || t.IsSubclassOf(type)); + if (matchingType != null) + { + discriminators.Add(discriminator); + } + } + } + finally + { + _configLock.ExitReadLock(); + } + + return discriminators.OrderBy(x => x).ToArray(); + } + + public IDiscriminatorConvention GetOrRegisterDiscriminatorConvention(Type type, + IDiscriminatorConvention discriminatorConvention) + { + _configLock.EnterReadLock(); + try + { + if (_discriminatorConventions.TryGetValue(type, out var registeredDiscriminatorConvention)) + { + return registeredDiscriminatorConvention; + } + } + finally + { + _configLock.ExitReadLock(); + } + + _configLock.EnterWriteLock(); + try + { + if (_discriminatorConventions.TryGetValue(type, out var registeredDiscrimantorConvention)) + { + return registeredDiscrimantorConvention; + } + + RegisterDiscriminatorConvention(type, discriminatorConvention); + return discriminatorConvention; + } + finally + { + _configLock.ExitWriteLock(); + } + } + + public bool IsDiscriminatorConventionRegisteredAtThisLevel(Type type) + { + _configLock.EnterReadLock(); + try + { + return _discriminatorConventions.ContainsKey(type); + } + finally + { + _configLock.ExitReadLock(); + } + } + + /// + /// Returns whether the given type has any discriminators registered for any of its subclasses. + /// + /// A Type. + /// True if the type is discriminated. + public bool IsTypeDiscriminated(Type type) + { + var typeInfo = type.GetTypeInfo(); + return typeInfo.IsInterface || _discriminatedTypes.Contains(type); + } + + /// + /// Looks up the actual type of an object to be deserialized. + /// + /// The nominal type of the object. + /// The discriminator. + /// The actual type of the object. + public Type LookupActualType(Type nominalType, BsonValue discriminator) + { + if (discriminator == null) + { + return nominalType; + } + + // note: EnsureKnownTypesAreRegistered handles its own locking so call from outside any lock + EnsureKnownTypesAreRegistered(nominalType); + + _configLock.EnterReadLock(); + try + { + Type actualType = null; + + HashSet hashSet; + var nominalTypeInfo = nominalType.GetTypeInfo(); + if (_discriminators.TryGetValue(discriminator, out hashSet)) + { + foreach (var type in hashSet) + { + if (nominalTypeInfo.IsAssignableFrom(type)) + { + if (actualType == null) + { + actualType = type; + } + else + { + string message = string.Format("Ambiguous discriminator '{0}'.", discriminator); + throw new BsonSerializationException(message); + } + } + } + + // no need for additional checks, we found the right type + if (actualType != null) + { + return actualType; + } + } + + if (discriminator.IsString) + { + actualType = TypeNameDiscriminator.GetActualType(discriminator.AsString); // see if it's a Type name + } + + if (actualType == null) + { + string message = string.Format("Unknown discriminator value '{0}'.", discriminator); + throw new BsonSerializationException(message); + } + + if (!nominalTypeInfo.IsAssignableFrom(actualType)) + { + string message = string.Format( + "Actual type {0} is not assignable to expected type {1}.", + actualType.FullName, nominalType.FullName); + throw new BsonSerializationException(message); + } + + return actualType; + } + finally + { + _configLock.ExitReadLock(); + } + } + + /// + /// Looks up the discriminator convention for a type. + /// + /// The type. + /// A discriminator convention. + public IDiscriminatorConvention LookupDiscriminatorConvention(Type type) + { + _configLock.EnterReadLock(); + try + { + IDiscriminatorConvention convention; + if (_discriminatorConventions.TryGetValue(type, out convention)) + { + return convention; + } + } + finally + { + _configLock.ExitReadLock(); + } + + _configLock.EnterWriteLock(); + try + { + IDiscriminatorConvention convention; + if (!_discriminatorConventions.TryGetValue(type, out convention)) + { + var typeInfo = type.GetTypeInfo(); + if (type == typeof(object)) + { + // if there is no convention registered for object register the default one + convention = new ObjectDiscriminatorConvention("_t"); + RegisterDiscriminatorConvention(typeof(object), convention); + } + else if (typeInfo.IsInterface) + { + // TODO: should convention for interfaces be inherited from parent interfaces? + convention = LookupDiscriminatorConvention(typeof(object)); + RegisterDiscriminatorConvention(type, convention); + } + else + { + // inherit the discriminator convention from the closest parent (that isn't object) that has one + // otherwise default to the standard scalar convention + Type parentType = typeInfo.BaseType; + while (true) + { + if (parentType == typeof(object)) + { + convention = StandardDiscriminatorConvention.Scalar; + break; + } + + if (_discriminatorConventions.TryGetValue(parentType, out convention)) + { + break; + } + + parentType = parentType.GetTypeInfo().BaseType; + } + + // register this convention for all types between this and the parent type where we found the convention + var unregisteredType = type; + while (unregisteredType != parentType) + { + RegisterDiscriminatorConvention(unregisteredType, convention); + unregisteredType = unregisteredType.GetTypeInfo().BaseType; + } + } + } + + return convention; + } + finally + { + _configLock.ExitWriteLock(); + } + } + + /// + /// Looks up an IdGenerator. + /// + /// The Id type. + /// An IdGenerator for the Id type. + public IIdGenerator LookupIdGenerator(Type type) + { + _configLock.EnterReadLock(); + try + { + IIdGenerator idGenerator; + if (_idGenerators.TryGetValue(type, out idGenerator)) + { + return idGenerator; + } + } + finally + { + _configLock.ExitReadLock(); + } + + _configLock.EnterWriteLock(); + try + { + IIdGenerator idGenerator; + if (!_idGenerators.TryGetValue(type, out idGenerator)) + { + var typeInfo = type.GetTypeInfo(); + if (typeInfo.IsValueType && _useZeroIdChecker) + { + var iEquatableDefinition = typeof(IEquatable<>); + var iEquatableType = iEquatableDefinition.MakeGenericType(type); + if (iEquatableType.GetTypeInfo().IsAssignableFrom(type)) + { + var zeroIdCheckerDefinition = typeof(ZeroIdChecker<>); + var zeroIdCheckerType = zeroIdCheckerDefinition.MakeGenericType(type); + idGenerator = (IIdGenerator)Activator.CreateInstance(zeroIdCheckerType); + } + } + else if (_useNullIdChecker) + { + idGenerator = NullIdChecker.Instance; + } + else + { + idGenerator = null; + } + + _idGenerators[type] = idGenerator; // remember it even if it's null + } + + return idGenerator; + } + finally + { + _configLock.ExitWriteLock(); + } + } + + /// + /// Looks up a serializer for a Type. + /// + /// The type. + /// A serializer for type T. + public IBsonSerializer LookupSerializer() + { + return (IBsonSerializer)LookupSerializer(typeof(T)); + } + + /// + /// Looks up a serializer for a Type. + /// + /// The Type. + /// A serializer for the Type. + public IBsonSerializer LookupSerializer(Type type) + { + return _serializerRegistry.GetSerializer(type); + } + + /// + /// Registers the discriminator for a type. + /// + /// The type. + /// The discriminator. + public void RegisterDiscriminator(Type type, BsonValue discriminator) + { + var typeInfo = type.GetTypeInfo(); + if (typeInfo.IsInterface) + { + var message = string.Format("Discriminators can only be registered for classes, not for interface {0}.", + type.FullName); + throw new BsonSerializationException(message); + } + + _configLock.EnterWriteLock(); + try + { + HashSet hashSet; + if (!_discriminators.TryGetValue(discriminator, out hashSet)) + { + hashSet = new HashSet(); + _discriminators.Add(discriminator, hashSet); + } + + if (!hashSet.Contains(type)) + { + hashSet.Add(type); + + // mark all base types as discriminated (so we know that it's worth reading a discriminator) + for (var baseType = typeInfo.BaseType; baseType != null; baseType = baseType.GetTypeInfo().BaseType) + { + _discriminatedTypes.Add(baseType); + } + } + } + finally + { + _configLock.ExitWriteLock(); + } + } + + /// + /// Registers the discriminator convention for a type. + /// + /// Type type. + /// The discriminator convention. + public void RegisterDiscriminatorConvention(Type type, IDiscriminatorConvention convention) + { + _configLock.EnterWriteLock(); + try + { + if (!_discriminatorConventions.ContainsKey(type)) + { + _discriminatorConventions.Add(type, convention); + } + else + { + var message = string.Format("There is already a discriminator convention registered for type {0}.", + type.FullName); + throw new BsonSerializationException(message); + } + } + finally + { + _configLock.ExitWriteLock(); + } + } + + /// + /// Registers a generic serializer definition for a generic type. + /// + /// The generic type. + /// The generic serializer definition. + public void RegisterGenericSerializerDefinition( + Type genericTypeDefinition, + Type genericSerializerDefinition) + { + _typeMappingSerializationProvider.RegisterMapping(genericTypeDefinition, genericSerializerDefinition); + } + + /// + /// Registers an IdGenerator for an Id Type. + /// + /// The Id Type. + /// The IdGenerator for the Id Type. + public void RegisterIdGenerator(Type type, IIdGenerator idGenerator) + { + _configLock.EnterWriteLock(); + try + { + _idGenerators[type] = idGenerator; + } + finally + { + _configLock.ExitWriteLock(); + } + } + + /// + /// Registers a serialization provider. + /// + /// The serialization provider. + public void RegisterSerializationProvider(IBsonSerializationProvider provider) + { + _serializerRegistry.RegisterSerializationProvider(provider); + } + + /// + /// Registers a serializer for a type. + /// + /// The type. + /// The serializer. + public void RegisterSerializer(IBsonSerializer serializer) + { + RegisterSerializer(typeof(T), serializer); + } + + /// + /// Registers a serializer for a type. + /// + /// The type. + /// The serializer. + public void RegisterSerializer(Type type, IBsonSerializer serializer) + { + _serializerRegistry.RegisterSerializer(type, serializer); + } + + /// + /// Serializes a value. + /// + /// The nominal type of the object. + /// The BsonWriter. + /// The object. + /// The serialization context configurator. + /// The serialization args. + public void Serialize( + IBsonWriter bsonWriter, + TNominalType value, + Action configurator = null, + BsonSerializationArgs args = default(BsonSerializationArgs)) + { + args.SetOrValidateNominalType(typeof(TNominalType), ""); + var serializer = LookupSerializer(); + var context = BsonSerializationContext.CreateRoot(bsonWriter, this, configurator); + serializer.Serialize(context, args, value); + } + + /// + /// Serializes a value. + /// + /// The BsonWriter. + /// The nominal type of the object. + /// The object. + /// The serialization context configurator. + /// The serialization args. + public void Serialize( + IBsonWriter bsonWriter, + Type nominalType, + object value, + Action configurator = null, + BsonSerializationArgs args = default(BsonSerializationArgs)) + { + args.SetOrValidateNominalType(nominalType, "nominalType"); + var serializer = LookupSerializer(nominalType); + var context = BsonSerializationContext.CreateRoot(bsonWriter, this, configurator); + serializer.Serialize(context, args, value); + } + + public IBsonClassMapDomain BsonClassMap => _classMapDomain; + + public IConventionRegistryDomain ConventionRegistry => _conventionRegistryDomain; + + public IBsonDefaults BsonDefaults => _bsonDefaults; + + /// + /// Tries to register a serializer for a type. + /// + /// The serializer. + /// The type. + /// True if the serializer was registered on this call, false if the same serializer was already registered on a previous call, throws an exception if a different serializer was already registered. + public bool TryRegisterSerializer(Type type, IBsonSerializer serializer) + { + return _serializerRegistry.TryRegisterSerializer(type, serializer); + } + + /// + /// Tries to register a serializer for a type. + /// + /// The type. + /// The serializer. + /// True if the serializer was registered on this call, false if the same serializer was already registered on a previous call, throws an exception if a different serializer was already registered. + public bool TryRegisterSerializer(IBsonSerializer serializer) + { + return TryRegisterSerializer(typeof(T), serializer); + } + + // internal methods + public void EnsureKnownTypesAreRegistered(Type nominalType) + { + if (_typesWithRegisteredKnownTypes.ContainsKey(nominalType)) + { + return; + } + + _configLock.EnterWriteLock(); + try + { + if (!_typesWithRegisteredKnownTypes.ContainsKey(nominalType)) + { + // only call LookupClassMap for classes with a BsonKnownTypesAttribute + var hasKnownTypesAttribute = nominalType.GetTypeInfo() + .GetCustomAttributes(typeof(BsonKnownTypesAttribute), inherit: false).Any(); + if (hasKnownTypesAttribute) + { + // try and force a scan of the known types + LookupSerializer(nominalType); + } + + // NOTE: The nominalType MUST be added to __typesWithRegisteredKnownTypes after all registration + // work is done to ensure that other threads don't access a partially registered nominalType + // when performing the initial check above outside the __config lock. + _typesWithRegisteredKnownTypes[nominalType] = null; + } + } + finally + { + _configLock.ExitWriteLock(); + } + } + + public void Dispose() + { + _configLock.Dispose(); + } + + // private methods + private void CreateSerializerRegistry() + { + _serializerRegistry = new BsonSerializerRegistry(this); + _typeMappingSerializationProvider = new TypeMappingSerializationProvider(); + + // order matters. It's in reverse order of how they'll get consumed + _serializerRegistry.RegisterSerializationProvider(new BsonClassMapSerializationProvider(this)); + _serializerRegistry.RegisterSerializationProvider(new DiscriminatedInterfaceSerializationProvider()); + _serializerRegistry.RegisterSerializationProvider(new CollectionsSerializationProvider()); + _serializerRegistry.RegisterSerializationProvider(new PrimitiveSerializationProvider()); + _serializerRegistry.RegisterSerializationProvider(new AttributedSerializationProvider()); + _serializerRegistry.RegisterSerializationProvider(_typeMappingSerializationProvider); + _serializerRegistry.RegisterSerializationProvider(new BsonObjectModelSerializationProvider()); + } + + private void CreateSubDomains() + { + _classMapDomain = new BsonClassMapDomain(this); + _conventionRegistryDomain = new ConventionRegistryDomain(); + _bsonDefaults = new BsonDefaultsDomain(this); + } + + private void RegisterIdGenerators() + { + RegisterIdGenerator(typeof(BsonObjectId), BsonObjectIdGenerator.Instance); + RegisterIdGenerator(typeof(Guid), GuidGenerator.Instance); + RegisterIdGenerator(typeof(ObjectId), ObjectIdGenerator.Instance); + } + } +} \ No newline at end of file diff --git a/src/MongoDB.Bson/Serialization/BsonSerializationInfo.cs b/src/MongoDB.Bson/Serialization/BsonSerializationInfo.cs index 01a5b9315de..f91a52ea4e2 100644 --- a/src/MongoDB.Bson/Serialization/BsonSerializationInfo.cs +++ b/src/MongoDB.Bson/Serialization/BsonSerializationInfo.cs @@ -118,7 +118,8 @@ public object DeserializeValue(BsonValue value) var tempDocument = new BsonDocument("value", value); using (var reader = new BsonDocumentReader(tempDocument)) { - var context = BsonDeserializationContext.CreateRoot(reader); + //QUESTION Is it correct we only need a default domain here? + var context = BsonDeserializationContext.CreateRoot(reader, BsonSerializer.DefaultSerializationDomain); reader.ReadStartDocument(); reader.ReadName("value"); var deserializedValue = _serializer.Deserialize(context); @@ -154,7 +155,8 @@ public BsonValue SerializeValue(object value) var tempDocument = new BsonDocument(); using (var bsonWriter = new BsonDocumentWriter(tempDocument)) { - var context = BsonSerializationContext.CreateRoot(bsonWriter); + //QUESTION Is it correct we only need a default domain here? + var context = BsonSerializationContext.CreateRoot(bsonWriter, BsonSerializer.DefaultSerializationDomain); bsonWriter.WriteStartDocument(); bsonWriter.WriteName("value"); _serializer.Serialize(context, value); @@ -173,7 +175,8 @@ public BsonArray SerializeValues(IEnumerable values) var tempDocument = new BsonDocument(); using (var bsonWriter = new BsonDocumentWriter(tempDocument)) { - var context = BsonSerializationContext.CreateRoot(bsonWriter); + //QUESTION Is it correct we only need a default domain here? + var context = BsonSerializationContext.CreateRoot(bsonWriter, BsonSerializer.DefaultSerializationDomain); bsonWriter.WriteStartDocument(); bsonWriter.WriteName("values"); bsonWriter.WriteStartArray(); diff --git a/src/MongoDB.Bson/Serialization/BsonSerializationProviderBase.cs b/src/MongoDB.Bson/Serialization/BsonSerializationProviderBase.cs index b2ffb587183..de65cd9ad1d 100644 --- a/src/MongoDB.Bson/Serialization/BsonSerializationProviderBase.cs +++ b/src/MongoDB.Bson/Serialization/BsonSerializationProviderBase.cs @@ -23,15 +23,17 @@ namespace MongoDB.Bson.Serialization /// public abstract class BsonSerializationProviderBase : IRegistryAwareBsonSerializationProvider { + //DOMAIN-API We should remove this and use the overload that takes a serializer registry instead. /// public virtual IBsonSerializer GetSerializer(Type type) { - return GetSerializer(type, BsonSerializer.SerializerRegistry); + return GetSerializer(type, BsonSerializer.DefaultSerializationDomain.SerializerRegistry); } /// public abstract IBsonSerializer GetSerializer(Type type, IBsonSerializerRegistry serializerRegistry); + //DOMAIN-API We should remove this and use the overload that takes a serializer registry instead. /// /// Creates the serializer from a serializer type definition and type arguments. /// @@ -40,7 +42,7 @@ public virtual IBsonSerializer GetSerializer(Type type) /// A serializer. protected virtual IBsonSerializer CreateGenericSerializer(Type serializerTypeDefinition, params Type[] typeArguments) { - return CreateGenericSerializer(serializerTypeDefinition, typeArguments, BsonSerializer.SerializerRegistry); + return CreateGenericSerializer(serializerTypeDefinition, typeArguments, BsonSerializer.DefaultSerializationDomain.SerializerRegistry); } /// @@ -58,6 +60,7 @@ protected virtual IBsonSerializer CreateGenericSerializer(Type serializerTypeDef return CreateSerializer(serializerType, serializerRegistry); } + //DOMAIN-API We should remove this and use the overload that takes a serializer registry instead. /// /// Creates the serializer. /// diff --git a/src/MongoDB.Bson/Serialization/BsonSerializer.cs b/src/MongoDB.Bson/Serialization/BsonSerializer.cs index 6602e74dec5..a862d05cb81 100644 --- a/src/MongoDB.Bson/Serialization/BsonSerializer.cs +++ b/src/MongoDB.Bson/Serialization/BsonSerializer.cs @@ -14,18 +14,10 @@ */ using System; -using System.Collections.Concurrent; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Reflection; using System.Threading; - -// don't add using statement for MongoDB.Bson.Serialization.Serializers to minimize dependencies on DefaultSerializer using MongoDB.Bson.IO; -using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Conventions; -using MongoDB.Bson.Serialization.IdGenerators; namespace MongoDB.Bson.Serialization { @@ -34,43 +26,29 @@ namespace MongoDB.Bson.Serialization /// public static class BsonSerializer { - // private static fields - private static ReaderWriterLockSlim __configLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); - private static Dictionary __idGenerators = new Dictionary(); - private static Dictionary __discriminatorConventions = new Dictionary(); - private static Dictionary> __discriminators = new Dictionary>(); - private static HashSet __discriminatedTypes = new HashSet(); - private static BsonSerializerRegistry __serializerRegistry; - private static TypeMappingSerializationProvider __typeMappingSerializationProvider; - // ConcurrentDictionary is being used as a concurrent set of Type. The values will always be null. - private static ConcurrentDictionary __typesWithRegisteredKnownTypes = new ConcurrentDictionary(); - - private static bool __useNullIdChecker = false; - private static bool __useZeroIdChecker = false; + private static readonly IBsonSerializationDomain _serializationDomain; // static constructor static BsonSerializer() { - CreateSerializerRegistry(); - RegisterIdGenerators(); + _serializationDomain = new BsonSerializationDomain("MAIN"); } + internal static IBsonSerializationDomain DefaultSerializationDomain => _serializationDomain; + // public static properties /// /// Gets the serializer registry. /// - public static IBsonSerializerRegistry SerializerRegistry - { - get { return __serializerRegistry; } - } + public static IBsonSerializerRegistry SerializerRegistry => _serializationDomain.SerializerRegistry; /// /// Gets or sets whether to use the NullIdChecker on reference Id types that don't have an IdGenerator registered. /// public static bool UseNullIdChecker { - get { return __useNullIdChecker; } - set { __useNullIdChecker = value; } + get => _serializationDomain.UseNullIdChecker; + set => _serializationDomain.UseNullIdChecker = value; } /// @@ -78,17 +56,21 @@ public static bool UseNullIdChecker /// public static bool UseZeroIdChecker { - get { return __useZeroIdChecker; } - set { __useZeroIdChecker = value; } + get => _serializationDomain.UseZeroIdChecker; + set => _serializationDomain.UseZeroIdChecker = value; } // internal static properties - internal static ReaderWriterLockSlim ConfigLock - { - get { return __configLock; } - } + internal static ReaderWriterLockSlim ConfigLock => _serializationDomain.ConfigLock; // public static methods + + /// + /// //TODO + /// + /// + internal static IBsonSerializationDomain CreateSerializationDomain() => new BsonSerializationDomain(); + /// /// Deserializes an object from a BsonDocument. /// @@ -97,12 +79,7 @@ internal static ReaderWriterLockSlim ConfigLock /// The configurator. /// A deserialized value. public static TNominalType Deserialize(BsonDocument document, Action configurator = null) - { - using (var bsonReader = new BsonDocumentReader(document)) - { - return Deserialize(bsonReader, configurator); - } - } + => _serializationDomain.Deserialize(document, configurator); /// /// Deserializes a value. @@ -112,11 +89,7 @@ public static TNominalType Deserialize(BsonDocument document, Acti /// The configurator. /// A deserialized value. public static TNominalType Deserialize(IBsonReader bsonReader, Action configurator = null) - { - var serializer = LookupSerializer(); - var context = BsonDeserializationContext.CreateRoot(bsonReader, configurator); - return serializer.Deserialize(context); - } + => _serializationDomain.Deserialize(bsonReader, configurator); /// /// Deserializes an object from a BSON byte array. @@ -126,13 +99,7 @@ public static TNominalType Deserialize(IBsonReader bsonReader, Act /// The configurator. /// A deserialized value. public static TNominalType Deserialize(byte[] bytes, Action configurator = null) - { - using (var buffer = new ByteArrayBuffer(bytes, isReadOnly: true)) - using (var stream = new ByteBufferStream(buffer)) - { - return Deserialize(stream, configurator); - } - } + => _serializationDomain.Deserialize(bytes, configurator); /// /// Deserializes an object from a BSON Stream. @@ -142,12 +109,7 @@ public static TNominalType Deserialize(byte[] bytes, ActionThe configurator. /// A deserialized value. public static TNominalType Deserialize(Stream stream, Action configurator = null) - { - using (var bsonReader = new BsonBinaryReader(stream)) - { - return Deserialize(bsonReader, configurator); - } - } + => _serializationDomain.Deserialize(stream, configurator); /// /// Deserializes an object from a JSON string. @@ -157,12 +119,7 @@ public static TNominalType Deserialize(Stream stream, ActionThe configurator. /// A deserialized value. public static TNominalType Deserialize(string json, Action configurator = null) - { - using (var bsonReader = new JsonReader(json)) - { - return Deserialize(bsonReader, configurator); - } - } + => _serializationDomain.Deserialize(json, configurator); /// /// Deserializes an object from a JSON TextReader. @@ -172,12 +129,7 @@ public static TNominalType Deserialize(string json, ActionThe configurator. /// A deserialized value. public static TNominalType Deserialize(TextReader textReader, Action configurator = null) - { - using (var bsonReader = new JsonReader(textReader)) - { - return Deserialize(bsonReader, configurator); - } - } + => _serializationDomain.Deserialize(textReader, configurator); /// /// Deserializes an object from a BsonDocument. @@ -187,12 +139,7 @@ public static TNominalType Deserialize(TextReader textReader, Acti /// The configurator. /// A deserialized value. public static object Deserialize(BsonDocument document, Type nominalType, Action configurator = null) - { - using (var bsonReader = new BsonDocumentReader(document)) - { - return Deserialize(bsonReader, nominalType, configurator); - } - } + => _serializationDomain.Deserialize(document, nominalType, configurator); /// /// Deserializes a value. @@ -202,11 +149,7 @@ public static object Deserialize(BsonDocument document, Type nominalType, Action /// The configurator. /// A deserialized value. public static object Deserialize(IBsonReader bsonReader, Type nominalType, Action configurator = null) - { - var serializer = LookupSerializer(nominalType); - var context = BsonDeserializationContext.CreateRoot(bsonReader, configurator); - return serializer.Deserialize(context); - } + => _serializationDomain.Deserialize(bsonReader, nominalType, configurator); /// /// Deserializes an object from a BSON byte array. @@ -216,13 +159,7 @@ public static object Deserialize(IBsonReader bsonReader, Type nominalType, Actio /// The configurator. /// A deserialized value. public static object Deserialize(byte[] bytes, Type nominalType, Action configurator = null) - { - using (var buffer = new ByteArrayBuffer(bytes, isReadOnly: true)) - using (var stream = new ByteBufferStream(buffer)) - { - return Deserialize(stream, nominalType, configurator); - } - } + => _serializationDomain.Deserialize(bytes, nominalType, configurator); /// /// Deserializes an object from a BSON Stream. @@ -232,12 +169,7 @@ public static object Deserialize(byte[] bytes, Type nominalType, ActionThe configurator. /// A deserialized value. public static object Deserialize(Stream stream, Type nominalType, Action configurator = null) - { - using (var bsonReader = new BsonBinaryReader(stream)) - { - return Deserialize(bsonReader, nominalType, configurator); - } - } + => _serializationDomain.Deserialize(stream, nominalType, configurator); /// /// Deserializes an object from a JSON string. @@ -247,12 +179,7 @@ public static object Deserialize(Stream stream, Type nominalType, ActionThe configurator. /// A deserialized value. public static object Deserialize(string json, Type nominalType, Action configurator = null) - { - using (var bsonReader = new JsonReader(json)) - { - return Deserialize(bsonReader, nominalType, configurator); - } - } + => _serializationDomain.Deserialize(json, nominalType, configurator); /// /// Deserializes an object from a JSON TextReader. @@ -262,57 +189,13 @@ public static object Deserialize(string json, Type nominalType, ActionThe configurator. /// A deserialized value. public static object Deserialize(TextReader textReader, Type nominalType, Action configurator = null) - { - using (var bsonReader = new JsonReader(textReader)) - { - return Deserialize(bsonReader, nominalType, configurator); - } - } + => _serializationDomain.Deserialize(textReader, nominalType, configurator); internal static IDiscriminatorConvention GetOrRegisterDiscriminatorConvention(Type type, IDiscriminatorConvention discriminatorConvention) - { - __configLock.EnterReadLock(); - try - { - if (__discriminatorConventions.TryGetValue(type, out var registeredDiscriminatorConvention)) - { - return registeredDiscriminatorConvention; - } - } - finally - { - __configLock.ExitReadLock(); - } - - __configLock.EnterWriteLock(); - try - { - if (__discriminatorConventions.TryGetValue(type, out var registeredDiscrimantorConvention)) - { - return registeredDiscrimantorConvention; - } - - RegisterDiscriminatorConvention(type, discriminatorConvention); - return discriminatorConvention; - } - finally - { - __configLock.ExitWriteLock(); - } - } + => _serializationDomain.GetOrRegisterDiscriminatorConvention(type, discriminatorConvention); internal static bool IsDiscriminatorConventionRegisteredAtThisLevel(Type type) - { - __configLock.EnterReadLock(); - try - { - return __discriminatorConventions.ContainsKey(type); - } - finally - { - __configLock.ExitReadLock(); - } - } + => _serializationDomain.IsDiscriminatorConventionRegisteredAtThisLevel(type); /// /// Returns whether the given type has any discriminators registered for any of its subclasses. @@ -320,10 +203,7 @@ internal static bool IsDiscriminatorConventionRegisteredAtThisLevel(Type type) /// A Type. /// True if the type is discriminated. public static bool IsTypeDiscriminated(Type type) - { - var typeInfo = type.GetTypeInfo(); - return typeInfo.IsInterface || __discriminatedTypes.Contains(type); - } + => _serializationDomain.IsTypeDiscriminated(type); /// /// Looks up the actual type of an object to be deserialized. @@ -332,73 +212,7 @@ public static bool IsTypeDiscriminated(Type type) /// The discriminator. /// The actual type of the object. public static Type LookupActualType(Type nominalType, BsonValue discriminator) - { - if (discriminator == null) - { - return nominalType; - } - - // note: EnsureKnownTypesAreRegistered handles its own locking so call from outside any lock - EnsureKnownTypesAreRegistered(nominalType); - - __configLock.EnterReadLock(); - try - { - Type actualType = null; - - HashSet hashSet; - var nominalTypeInfo = nominalType.GetTypeInfo(); - if (__discriminators.TryGetValue(discriminator, out hashSet)) - { - foreach (var type in hashSet) - { - if (nominalTypeInfo.IsAssignableFrom(type)) - { - if (actualType == null) - { - actualType = type; - } - else - { - string message = string.Format("Ambiguous discriminator '{0}'.", discriminator); - throw new BsonSerializationException(message); - } - } - } - - // no need for additional checks, we found the right type - if (actualType != null) - { - return actualType; - } - } - - if (discriminator.IsString) - { - actualType = TypeNameDiscriminator.GetActualType(discriminator.AsString); // see if it's a Type name - } - - if (actualType == null) - { - string message = string.Format("Unknown discriminator value '{0}'.", discriminator); - throw new BsonSerializationException(message); - } - - if (!nominalTypeInfo.IsAssignableFrom(actualType)) - { - string message = string.Format( - "Actual type {0} is not assignable to expected type {1}.", - actualType.FullName, nominalType.FullName); - throw new BsonSerializationException(message); - } - - return actualType; - } - finally - { - __configLock.ExitReadLock(); - } - } + => _serializationDomain.LookupActualType(nominalType, discriminator); /// /// Looks up the discriminator convention for a type. @@ -406,76 +220,7 @@ public static Type LookupActualType(Type nominalType, BsonValue discriminator) /// The type. /// A discriminator convention. public static IDiscriminatorConvention LookupDiscriminatorConvention(Type type) - { - __configLock.EnterReadLock(); - try - { - IDiscriminatorConvention convention; - if (__discriminatorConventions.TryGetValue(type, out convention)) - { - return convention; - } - } - finally - { - __configLock.ExitReadLock(); - } - - __configLock.EnterWriteLock(); - try - { - IDiscriminatorConvention convention; - if (!__discriminatorConventions.TryGetValue(type, out convention)) - { - var typeInfo = type.GetTypeInfo(); - if (type == typeof(object)) - { - // if there is no convention registered for object register the default one - convention = new ObjectDiscriminatorConvention("_t"); - RegisterDiscriminatorConvention(typeof(object), convention); - } - else if (typeInfo.IsInterface) - { - // TODO: should convention for interfaces be inherited from parent interfaces? - convention = LookupDiscriminatorConvention(typeof(object)); - RegisterDiscriminatorConvention(type, convention); - } - else - { - // inherit the discriminator convention from the closest parent (that isn't object) that has one - // otherwise default to the standard scalar convention - Type parentType = typeInfo.BaseType; - while (true) - { - if (parentType == typeof(object)) - { - convention = StandardDiscriminatorConvention.Scalar; - break; - } - if (__discriminatorConventions.TryGetValue(parentType, out convention)) - { - break; - } - parentType = parentType.GetTypeInfo().BaseType; - } - - // register this convention for all types between this and the parent type where we found the convention - var unregisteredType = type; - while (unregisteredType != parentType) - { - RegisterDiscriminatorConvention(unregisteredType, convention); - unregisteredType = unregisteredType.GetTypeInfo().BaseType; - } - } - } - - return convention; - } - finally - { - __configLock.ExitWriteLock(); - } - } + => _serializationDomain.LookupDiscriminatorConvention(type); /// /// Looks up an IdGenerator. @@ -483,78 +228,21 @@ public static IDiscriminatorConvention LookupDiscriminatorConvention(Type type) /// The Id type. /// An IdGenerator for the Id type. public static IIdGenerator LookupIdGenerator(Type type) - { - __configLock.EnterReadLock(); - try - { - IIdGenerator idGenerator; - if (__idGenerators.TryGetValue(type, out idGenerator)) - { - return idGenerator; - } - } - finally - { - __configLock.ExitReadLock(); - } - - __configLock.EnterWriteLock(); - try - { - IIdGenerator idGenerator; - if (!__idGenerators.TryGetValue(type, out idGenerator)) - { - var typeInfo = type.GetTypeInfo(); - if (typeInfo.IsValueType && __useZeroIdChecker) - { - var iEquatableDefinition = typeof(IEquatable<>); - var iEquatableType = iEquatableDefinition.MakeGenericType(type); - if (iEquatableType.GetTypeInfo().IsAssignableFrom(type)) - { - var zeroIdCheckerDefinition = typeof(ZeroIdChecker<>); - var zeroIdCheckerType = zeroIdCheckerDefinition.MakeGenericType(type); - idGenerator = (IIdGenerator)Activator.CreateInstance(zeroIdCheckerType); - } - } - else if (__useNullIdChecker) - { - idGenerator = NullIdChecker.Instance; - } - else - { - idGenerator = null; - } - - __idGenerators[type] = idGenerator; // remember it even if it's null - } - - return idGenerator; - } - finally - { - __configLock.ExitWriteLock(); - } - } + => _serializationDomain.LookupIdGenerator(type); /// /// Looks up a serializer for a Type. /// /// The type. /// A serializer for type T. - public static IBsonSerializer LookupSerializer() - { - return (IBsonSerializer)LookupSerializer(typeof(T)); - } + public static IBsonSerializer LookupSerializer() => _serializationDomain.LookupSerializer(); /// /// Looks up a serializer for a Type. /// /// The Type. /// A serializer for the Type. - public static IBsonSerializer LookupSerializer(Type type) - { - return __serializerRegistry.GetSerializer(type); - } + public static IBsonSerializer LookupSerializer(Type type) => _serializationDomain.LookupSerializer(type); /// /// Registers the discriminator for a type. @@ -562,40 +250,7 @@ public static IBsonSerializer LookupSerializer(Type type) /// The type. /// The discriminator. public static void RegisterDiscriminator(Type type, BsonValue discriminator) - { - var typeInfo = type.GetTypeInfo(); - if (typeInfo.IsInterface) - { - var message = string.Format("Discriminators can only be registered for classes, not for interface {0}.", type.FullName); - throw new BsonSerializationException(message); - } - - __configLock.EnterWriteLock(); - try - { - HashSet hashSet; - if (!__discriminators.TryGetValue(discriminator, out hashSet)) - { - hashSet = new HashSet(); - __discriminators.Add(discriminator, hashSet); - } - - if (!hashSet.Contains(type)) - { - hashSet.Add(type); - - // mark all base types as discriminated (so we know that it's worth reading a discriminator) - for (var baseType = typeInfo.BaseType; baseType != null; baseType = baseType.GetTypeInfo().BaseType) - { - __discriminatedTypes.Add(baseType); - } - } - } - finally - { - __configLock.ExitWriteLock(); - } - } + => _serializationDomain.RegisterDiscriminator(type, discriminator); /// /// Registers the discriminator convention for a type. @@ -603,37 +258,15 @@ public static void RegisterDiscriminator(Type type, BsonValue discriminator) /// Type type. /// The discriminator convention. public static void RegisterDiscriminatorConvention(Type type, IDiscriminatorConvention convention) - { - __configLock.EnterWriteLock(); - try - { - if (!__discriminatorConventions.ContainsKey(type)) - { - __discriminatorConventions.Add(type, convention); - } - else - { - var message = string.Format("There is already a discriminator convention registered for type {0}.", type.FullName); - throw new BsonSerializationException(message); - } - } - finally - { - __configLock.ExitWriteLock(); - } - } + => _serializationDomain.RegisterDiscriminatorConvention(type, convention); /// /// Registers a generic serializer definition for a generic type. /// /// The generic type. /// The generic serializer definition. - public static void RegisterGenericSerializerDefinition( - Type genericTypeDefinition, - Type genericSerializerDefinition) - { - __typeMappingSerializationProvider.RegisterMapping(genericTypeDefinition, genericSerializerDefinition); - } + public static void RegisterGenericSerializerDefinition(Type genericTypeDefinition, Type genericSerializerDefinition) + => _serializationDomain.RegisterGenericSerializerDefinition(genericTypeDefinition, genericSerializerDefinition); /// /// Registers an IdGenerator for an Id Type. @@ -641,26 +274,14 @@ public static void RegisterGenericSerializerDefinition( /// The Id Type. /// The IdGenerator for the Id Type. public static void RegisterIdGenerator(Type type, IIdGenerator idGenerator) - { - __configLock.EnterWriteLock(); - try - { - __idGenerators[type] = idGenerator; - } - finally - { - __configLock.ExitWriteLock(); - } - } + => _serializationDomain.RegisterIdGenerator(type, idGenerator); /// /// Registers a serialization provider. /// /// The serialization provider. public static void RegisterSerializationProvider(IBsonSerializationProvider provider) - { - __serializerRegistry.RegisterSerializationProvider(provider); - } + => _serializationDomain.RegisterSerializationProvider(provider); /// /// Registers a serializer for a type. @@ -668,9 +289,7 @@ public static void RegisterSerializationProvider(IBsonSerializationProvider prov /// The type. /// The serializer. public static void RegisterSerializer(IBsonSerializer serializer) - { - RegisterSerializer(typeof(T), serializer); - } + => _serializationDomain.RegisterSerializer(serializer); /// /// Registers a serializer for a type. @@ -678,9 +297,7 @@ public static void RegisterSerializer(IBsonSerializer serializer) /// The type. /// The serializer. public static void RegisterSerializer(Type type, IBsonSerializer serializer) - { - __serializerRegistry.RegisterSerializer(type, serializer); - } + => _serializationDomain.RegisterSerializer(type, serializer); /// /// Serializes a value. @@ -694,13 +311,8 @@ public static void Serialize( IBsonWriter bsonWriter, TNominalType value, Action configurator = null, - BsonSerializationArgs args = default(BsonSerializationArgs)) - { - args.SetOrValidateNominalType(typeof(TNominalType), ""); - var serializer = LookupSerializer(); - var context = BsonSerializationContext.CreateRoot(bsonWriter, configurator); - serializer.Serialize(context, args, value); - } + BsonSerializationArgs args = default) + => _serializationDomain.Serialize(bsonWriter, value, configurator, args); /// /// Serializes a value. @@ -716,12 +328,7 @@ public static void Serialize( object value, Action configurator = null, BsonSerializationArgs args = default(BsonSerializationArgs)) - { - args.SetOrValidateNominalType(nominalType, "nominalType"); - var serializer = LookupSerializer(nominalType); - var context = BsonSerializationContext.CreateRoot(bsonWriter, configurator); - serializer.Serialize(context, args, value); - } + => _serializationDomain.Serialize(bsonWriter, nominalType, value, configurator, args); /// /// Tries to register a serializer for a type. @@ -730,9 +337,7 @@ public static void Serialize( /// The type. /// True if the serializer was registered on this call, false if the same serializer was already registered on a previous call, throws an exception if a different serializer was already registered. public static bool TryRegisterSerializer(Type type, IBsonSerializer serializer) - { - return __serializerRegistry.TryRegisterSerializer(type, serializer); - } + => _serializationDomain.TryRegisterSerializer(type, serializer); /// /// Tries to register a serializer for a type. @@ -741,95 +346,13 @@ public static bool TryRegisterSerializer(Type type, IBsonSerializer serializer) /// The serializer. /// True if the serializer was registered on this call, false if the same serializer was already registered on a previous call, throws an exception if a different serializer was already registered. public static bool TryRegisterSerializer(IBsonSerializer serializer) - { - return TryRegisterSerializer(typeof(T), serializer); - } + => _serializationDomain.TryRegisterSerializer(serializer); // internal static methods internal static void EnsureKnownTypesAreRegistered(Type nominalType) - { - if (__typesWithRegisteredKnownTypes.ContainsKey(nominalType)) - { - return; - } - - __configLock.EnterWriteLock(); - try - { - if (!__typesWithRegisteredKnownTypes.ContainsKey(nominalType)) - { - // only call LookupClassMap for classes with a BsonKnownTypesAttribute - var hasKnownTypesAttribute = nominalType.GetTypeInfo().GetCustomAttributes(typeof(BsonKnownTypesAttribute), inherit: false).Any(); - if (hasKnownTypesAttribute) - { - // try and force a scan of the known types - LookupSerializer(nominalType); - } - - // NOTE: The nominalType MUST be added to __typesWithRegisteredKnownTypes after all registration - // work is done to ensure that other threads don't access a partially registered nominalType - // when performing the initial check above outside the __config lock. - __typesWithRegisteredKnownTypes[nominalType] = null; - } - } - finally - { - __configLock.ExitWriteLock(); - } - } + => _serializationDomain.EnsureKnownTypesAreRegistered(nominalType); - // internal static methods internal static BsonValue[] GetDiscriminatorsForTypeAndSubTypes(Type type) - { - // note: EnsureKnownTypesAreRegistered handles its own locking so call from outside any lock - EnsureKnownTypesAreRegistered(type); - - var discriminators = new List(); - - __configLock.EnterReadLock(); - try - { - foreach (var entry in __discriminators) - { - var discriminator = entry.Key; - var actualTypes = entry.Value; - - var matchingType = actualTypes.SingleOrDefault(t => t == type || t.IsSubclassOf(type)); - if (matchingType != null) - { - discriminators.Add(discriminator); - } - } - } - finally - { - __configLock.ExitReadLock(); - } - - return discriminators.OrderBy(x => x).ToArray(); - } - - // private static methods - private static void CreateSerializerRegistry() - { - __serializerRegistry = new BsonSerializerRegistry(); - __typeMappingSerializationProvider = new TypeMappingSerializationProvider(); - - // order matters. It's in reverse order of how they'll get consumed - __serializerRegistry.RegisterSerializationProvider(new BsonClassMapSerializationProvider()); - __serializerRegistry.RegisterSerializationProvider(new DiscriminatedInterfaceSerializationProvider()); - __serializerRegistry.RegisterSerializationProvider(new CollectionsSerializationProvider()); - __serializerRegistry.RegisterSerializationProvider(new PrimitiveSerializationProvider()); - __serializerRegistry.RegisterSerializationProvider(new AttributedSerializationProvider()); - __serializerRegistry.RegisterSerializationProvider(__typeMappingSerializationProvider); - __serializerRegistry.RegisterSerializationProvider(new BsonObjectModelSerializationProvider()); - } - - private static void RegisterIdGenerators() - { - RegisterIdGenerator(typeof(BsonObjectId), BsonObjectIdGenerator.Instance); - RegisterIdGenerator(typeof(Guid), GuidGenerator.Instance); - RegisterIdGenerator(typeof(ObjectId), ObjectIdGenerator.Instance); - } + => _serializationDomain.GetDiscriminatorsForTypeAndSubTypes(type); } } diff --git a/src/MongoDB.Bson/Serialization/BsonSerializerRegistry.cs b/src/MongoDB.Bson/Serialization/BsonSerializerRegistry.cs index 429386f6019..e5b9aad1caa 100644 --- a/src/MongoDB.Bson/Serialization/BsonSerializerRegistry.cs +++ b/src/MongoDB.Bson/Serialization/BsonSerializerRegistry.cs @@ -33,14 +33,22 @@ public sealed class BsonSerializerRegistry : IBsonSerializerRegistry /// /// Initializes a new instance of the class. /// - public BsonSerializerRegistry() + public BsonSerializerRegistry(): + this(BsonSerializer.DefaultSerializationDomain) + { + } + + /// + /// //TODO + /// + /// + internal BsonSerializerRegistry(IBsonSerializationDomain serializationDomain) { _cache = new ConcurrentDictionary(); _serializationProviders = new ConcurrentStack(); _createSerializer = CreateSerializer; } - // public methods /// /// Gets the serializer for the specified . /// If none is already registered, the serialization providers will be used to create a serializer and it will be automatically registered. @@ -156,17 +164,14 @@ private IBsonSerializer CreateSerializer(Type type) { foreach (var serializationProvider in _serializationProviders) { - IBsonSerializer serializer; - - var registryAwareSerializationProvider = serializationProvider as IRegistryAwareBsonSerializationProvider; - if (registryAwareSerializationProvider != null) - { - serializer = registryAwareSerializationProvider.GetSerializer(type, this); - } - else + var serializer = serializationProvider switch { - serializer = serializationProvider.GetSerializer(type); - } + IDomainAwareBsonSerializationProvider domainAwareBsonSerializationProvider => + domainAwareBsonSerializationProvider.GetSerializerWithDomain(type), + IRegistryAwareBsonSerializationProvider registryAwareSerializationProvider => + registryAwareSerializationProvider.GetSerializer(type, this), + _ => serializationProvider.GetSerializer(type) + }; if (serializer != null) { diff --git a/src/MongoDB.Bson/Serialization/Conventions/AttributeConventionPack.cs b/src/MongoDB.Bson/Serialization/Conventions/AttributeConventionPack.cs index 77f0bc0cb78..b5646e41489 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/AttributeConventionPack.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/AttributeConventionPack.cs @@ -60,7 +60,7 @@ public IEnumerable Conventions } // nested classes - private class AttributeConvention : ConventionBase, IClassMapConvention, ICreatorMapConvention, IMemberMapConvention, IPostProcessingConvention + private class AttributeConvention : ConventionBase, IClassMapConvention, ICreatorMapConvention, IMemberMapConventionInternal, IPostProcessingConventionInternal { // public methods public void Apply(BsonClassMap classMap) @@ -87,7 +87,9 @@ public void Apply(BsonCreatorMap creatorMap) } } - public void Apply(BsonMemberMap memberMap) + public void Apply(BsonMemberMap memberMap) => Apply(memberMap, BsonSerializer.DefaultSerializationDomain); + + public void Apply(BsonMemberMap memberMap, IBsonSerializationDomain domain) { var attributes = memberMap.MemberInfo.GetCustomAttributes(inherit: false).OfType(); var groupings = attributes.GroupBy(a => (a is BsonSerializerAttribute) ? 1 : 2); @@ -100,7 +102,9 @@ public void Apply(BsonMemberMap memberMap) } } - public void PostProcess(BsonClassMap classMap) + public void PostProcess(BsonClassMap classMap) => PostProcess(classMap, BsonSerializer.DefaultSerializationDomain); + + public void PostProcess(BsonClassMap classMap, IBsonSerializationDomain domain) { foreach (var attribute in classMap.ClassType.GetTypeInfo().GetCustomAttributes(inherit: false).OfType()) { diff --git a/src/MongoDB.Bson/Serialization/Conventions/CamelCaseElementNameConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/CamelCaseElementNameConvention.cs index dd249c11359..9154e353deb 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/CamelCaseElementNameConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/CamelCaseElementNameConvention.cs @@ -14,21 +14,20 @@ */ using System; -using System.Reflection; namespace MongoDB.Bson.Serialization.Conventions { /// /// A convention that sets the element name the same as the member name with the first character lower cased. /// - public class CamelCaseElementNameConvention : ConventionBase, IMemberMapConvention + public class CamelCaseElementNameConvention : ConventionBase, IMemberMapConventionInternal { // public methods - /// - /// Applies a modification to the member map. - /// - /// The member map. - public void Apply(BsonMemberMap memberMap) + /// + public void Apply(BsonMemberMap memberMap) => (this as IMemberMapConventionInternal).Apply(memberMap, BsonSerializer.DefaultSerializationDomain); + + /// + void IMemberMapConventionInternal.Apply(BsonMemberMap memberMap, IBsonSerializationDomain domain) { string name = memberMap.MemberName; name = GetElementName(name); diff --git a/src/MongoDB.Bson/Serialization/Conventions/ConventionRegistry.cs b/src/MongoDB.Bson/Serialization/Conventions/ConventionRegistry.cs index 217a7b69d86..d389df271ef 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/ConventionRegistry.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/ConventionRegistry.cs @@ -14,7 +14,6 @@ */ using System; -using System.Collections.Generic; namespace MongoDB.Bson.Serialization.Conventions { @@ -23,60 +22,14 @@ namespace MongoDB.Bson.Serialization.Conventions /// public static class ConventionRegistry { - // private static fields - private readonly static List __conventionPacks = new List(); - private readonly static object __lock = new object(); - - // static constructors - static ConventionRegistry() - { - Register("__defaults__", DefaultConventionPack.Instance, t => true); - Register("__attributes__", AttributeConventionPack.Instance, t => true); - } - // public static methods /// /// Looks up the effective set of conventions that apply to a type. /// /// The type. /// The conventions for that type. - public static IConventionPack Lookup(Type type) - { - if (type == null) - { - throw new ArgumentNullException("type"); - } - - lock (__lock) - { - var pack = new ConventionPack(); - - // append any attribute packs (usually just one) at the end so attributes are processed last - var attributePacks = new List(); - foreach (var container in __conventionPacks) - { - if (container.Filter(type)) - { - - if (container.Name == "__attributes__") - { - attributePacks.Add(container.Pack); - } - else - { - pack.Append(container.Pack); - } - } - } - - foreach (var attributePack in attributePacks) - { - pack.Append(attributePack); - } - - return pack; - } - } + public static IConventionPack Lookup(Type type) => + BsonSerializer.DefaultSerializationDomain.ConventionRegistry.Lookup(type); /// /// Registers the conventions. @@ -84,33 +37,8 @@ public static IConventionPack Lookup(Type type) /// The name. /// The conventions. /// The filter. - public static void Register(string name, IConventionPack conventions, Func filter) - { - if (name == null) - { - throw new ArgumentNullException("name"); - } - if (conventions == null) - { - throw new ArgumentNullException("conventions"); - } - if (filter == null) - { - throw new ArgumentNullException("filter"); - } - - lock (__lock) - { - var container = new ConventionPackContainer - { - Filter = filter, - Name = name, - Pack = conventions - }; - - __conventionPacks.Add(container); - } - } + public static void Register(string name, IConventionPack conventions, Func filter) => + BsonSerializer.DefaultSerializationDomain.ConventionRegistry.Register(name, conventions, filter); /// /// Removes the conventions specified by the given name. @@ -119,25 +47,7 @@ public static void Register(string name, IConventionPack conventions, FuncRemoving a convention allows the removal of the special __defaults__ conventions /// and the __attributes__ conventions for those who want to completely customize the /// experience. - public static void Remove(string name) - { - if (name == null) - { - throw new ArgumentNullException("name"); - } - - lock (__lock) - { - __conventionPacks.RemoveAll(x => x.Name == name); - } - } - - // private class - private class ConventionPackContainer - { - public Func Filter; - public string Name; - public IConventionPack Pack; - } + public static void Remove(string name) => + BsonSerializer.DefaultSerializationDomain.ConventionRegistry.Remove(name); } } diff --git a/src/MongoDB.Bson/Serialization/Conventions/ConventionRegistryDomain.cs b/src/MongoDB.Bson/Serialization/Conventions/ConventionRegistryDomain.cs new file mode 100644 index 00000000000..fec96b02e31 --- /dev/null +++ b/src/MongoDB.Bson/Serialization/Conventions/ConventionRegistryDomain.cs @@ -0,0 +1,141 @@ +/* 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; + +namespace MongoDB.Bson.Serialization.Conventions +{ + internal class ConventionRegistryDomain : IConventionRegistryDomain + { + private readonly List _conventionPacks = []; + private readonly object _lock = new(); + + // constructors + internal ConventionRegistryDomain() + { + Register("__defaults__", DefaultConventionPack.Instance, t => true); + Register("__attributes__", AttributeConventionPack.Instance, t => true); + } + + // public static methods + /// + /// Looks up the effective set of conventions that apply to a type. + /// + /// The type. + /// The conventions for that type. + public IConventionPack Lookup(Type type) + { + if (type == null) + { + throw new ArgumentNullException("type"); + } + + lock (_lock) + { + var pack = new ConventionPack(); + + // append any attribute packs (usually just one) at the end so attributes are processed last + var attributePacks = new List(); + foreach (var container in _conventionPacks) + { + if (container.Filter(type)) + { + + if (container.Name == "__attributes__") + { + attributePacks.Add(container.Pack); + } + else + { + pack.Append(container.Pack); + } + } + } + + foreach (var attributePack in attributePacks) + { + pack.Append(attributePack); + } + + return pack; + } + } + + /// + /// Registers the conventions. + /// + /// The name. + /// The conventions. + /// The filter. + public void Register(string name, IConventionPack conventions, Func filter) + { + if (name == null) + { + throw new ArgumentNullException("name"); + } + + if (conventions == null) + { + throw new ArgumentNullException("conventions"); + } + + if (filter == null) + { + throw new ArgumentNullException("filter"); + } + + lock (_lock) + { + var container = new ConventionPackContainer + { + Filter = filter, + Name = name, + Pack = conventions + }; + + _conventionPacks.Add(container); + } + } + + /// + /// Removes the conventions specified by the given name. + /// + /// The name. + /// Removing a convention allows the removal of the special __defaults__ conventions + /// and the __attributes__ conventions for those who want to completely customize the + /// experience. + public void Remove(string name) + { + if (name == null) + { + throw new ArgumentNullException("name"); + } + + lock (_lock) + { + _conventionPacks.RemoveAll(x => x.Name == name); + } + } + + // private class + private class ConventionPackContainer + { + public Func Filter; + public string Name; + public IConventionPack Pack; + } + } +} \ No newline at end of file diff --git a/src/MongoDB.Bson/Serialization/Conventions/ConventionRunner.cs b/src/MongoDB.Bson/Serialization/Conventions/ConventionRunner.cs index 091be418810..8b8431bae16 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/ConventionRunner.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/ConventionRunner.cs @@ -47,7 +47,9 @@ public ConventionRunner(IConventionPack conventions) /// Applies a modification to the class map. /// /// The class map. - public void Apply(BsonClassMap classMap) + public void Apply(BsonClassMap classMap) => Apply(classMap, BsonSerializer.DefaultSerializationDomain); + + internal void Apply(BsonClassMap classMap, IBsonSerializationDomain serializationDomain) { foreach (var convention in _conventions.OfType()) { @@ -58,7 +60,7 @@ public void Apply(BsonClassMap classMap) { foreach (var memberMap in classMap.DeclaredMemberMaps) { - convention.Apply(memberMap); + convention.ApplyInternal(memberMap, serializationDomain); } } @@ -72,7 +74,7 @@ public void Apply(BsonClassMap classMap) foreach (var convention in _conventions.OfType()) { - convention.PostProcess(classMap); + convention.PostProcessInternal(classMap, serializationDomain); } } } diff --git a/src/MongoDB.Bson/Serialization/Conventions/DelegateMemberMapConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/DelegateMemberMapConvention.cs index 80cafe70046..f2c90e32fe5 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/DelegateMemberMapConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/DelegateMemberMapConvention.cs @@ -20,7 +20,7 @@ namespace MongoDB.Bson.Serialization.Conventions /// /// A member map convention that wraps a delegate. /// - public class DelegateMemberMapConvention : ConventionBase, IMemberMapConvention + public class DelegateMemberMapConvention : ConventionBase, IMemberMapConventionInternal { // private fields private readonly Action _action; @@ -46,7 +46,10 @@ public DelegateMemberMapConvention(string name, Action action) /// Applies a modification to the member map. /// /// The member map. - public void Apply(BsonMemberMap memberMap) + public void Apply(BsonMemberMap memberMap) => (this as IMemberMapConventionInternal).Apply(memberMap, BsonSerializer.DefaultSerializationDomain); + + /// + void IMemberMapConventionInternal.Apply(BsonMemberMap memberMap, IBsonSerializationDomain domain) { _action(memberMap); } diff --git a/src/MongoDB.Bson/Serialization/Conventions/DelegatePostProcessingConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/DelegatePostProcessingConvention.cs index 8c535f842f0..d8e6c1408f8 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/DelegatePostProcessingConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/DelegatePostProcessingConvention.cs @@ -20,7 +20,7 @@ namespace MongoDB.Bson.Serialization.Conventions /// /// A post processing convention that wraps a delegate. /// - public class DelegatePostProcessingConvention : ConventionBase, IPostProcessingConvention + public class DelegatePostProcessingConvention : ConventionBase, IPostProcessingConventionInternal { // private fields private readonly Action _action; @@ -46,7 +46,10 @@ public DelegatePostProcessingConvention(string name, Action action /// Applies a post processing modification to the class map. /// /// The class map. - public void PostProcess(BsonClassMap classMap) + public void PostProcess(BsonClassMap classMap) => (this as IPostProcessingConventionInternal).PostProcess(classMap, BsonSerializer.DefaultSerializationDomain); + + /// + void IPostProcessingConventionInternal.PostProcess(BsonClassMap classMap, IBsonSerializationDomain domain) { _action(classMap); } diff --git a/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs index 4f5274a0664..b0c6bfabd5a 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/EnumRepresentationConvention.cs @@ -21,7 +21,7 @@ namespace MongoDB.Bson.Serialization.Conventions /// /// A convention that allows you to set the Enum serialization representation /// - public class EnumRepresentationConvention : ConventionBase, IMemberMapConvention + public class EnumRepresentationConvention : ConventionBase, IMemberMapConventionInternal { // private fields private readonly BsonType _representation; @@ -66,7 +66,10 @@ public EnumRepresentationConvention(BsonType representation, bool topLevelOnly) /// Applies a modification to the member map. /// /// The member map. - public void Apply(BsonMemberMap memberMap) + public void Apply(BsonMemberMap memberMap) => (this as IMemberMapConventionInternal).Apply(memberMap, BsonSerializer.DefaultSerializationDomain); + + /// + void IMemberMapConventionInternal.Apply(BsonMemberMap memberMap, IBsonSerializationDomain domain) { var memberType = memberMap.MemberType; diff --git a/src/MongoDB.Bson/Serialization/Conventions/HierarchicalDiscriminatorConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/HierarchicalDiscriminatorConvention.cs index beebf06230e..85751240e74 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/HierarchicalDiscriminatorConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/HierarchicalDiscriminatorConvention.cs @@ -16,13 +16,14 @@ using System; using System.Collections.Generic; using System.Linq; +using MongoDB.Bson.IO; namespace MongoDB.Bson.Serialization.Conventions { /// /// Represents a discriminator convention where the discriminator is an array of all the discriminators provided by the class maps of the root class down to the actual type. /// - public class HierarchicalDiscriminatorConvention : StandardDiscriminatorConvention, IHierarchicalDiscriminatorConvention + public class HierarchicalDiscriminatorConvention : StandardDiscriminatorConvention, IHierarchicalDiscriminatorConvention, IDiscriminatorConventionInternal { // constructors /// @@ -34,6 +35,11 @@ public HierarchicalDiscriminatorConvention(string elementName) { } + Type IDiscriminatorConventionInternal.GetActualType(IBsonReader bsonReader, Type nominalType, IBsonSerializationDomain domain) + { + return base.GetActualType(bsonReader, nominalType, domain); + } + // public methods /// /// Gets the discriminator value for an actual type. @@ -41,10 +47,14 @@ public HierarchicalDiscriminatorConvention(string elementName) /// The nominal type. /// The actual type. /// The discriminator value. - public override BsonValue GetDiscriminator(Type nominalType, Type actualType) + public override BsonValue GetDiscriminator(Type nominalType, Type actualType) => + (this as IDiscriminatorConventionInternal).GetDiscriminator(nominalType, actualType, BsonSerializer.DefaultSerializationDomain); + + /// + BsonValue IDiscriminatorConventionInternal.GetDiscriminator(Type nominalType, Type actualType, IBsonSerializationDomain domain) { // TODO: this isn't quite right, not all classes are serialized using a class map serializer - var classMap = BsonClassMap.LookupClassMap(actualType); + var classMap = domain.BsonClassMap.LookupClassMap(actualType); if (actualType != nominalType || classMap.DiscriminatorIsRequired || classMap.HasRootClass) { if (classMap.HasRootClass && !classMap.IsRootClass) diff --git a/src/MongoDB.Bson/Serialization/Conventions/IConventionRegistryDomain.cs b/src/MongoDB.Bson/Serialization/Conventions/IConventionRegistryDomain.cs new file mode 100644 index 00000000000..b2720052419 --- /dev/null +++ b/src/MongoDB.Bson/Serialization/Conventions/IConventionRegistryDomain.cs @@ -0,0 +1,28 @@ +using System; + +namespace MongoDB.Bson.Serialization.Conventions +{ + internal interface IConventionRegistryDomain + { + /// + /// //TODO + /// + /// + /// + IConventionPack Lookup(Type type); + + /// + /// //TODO + /// + /// + /// + /// + void Register(string name, IConventionPack conventions, Func filter); + + /// + /// //TODO + /// + /// + void Remove(string name); + } +} \ No newline at end of file diff --git a/src/MongoDB.Bson/Serialization/Conventions/IDiscriminatorConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/IDiscriminatorConvention.cs index 242d0b557b2..cfd44ef8936 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/IDiscriminatorConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/IDiscriminatorConvention.cs @@ -44,4 +44,26 @@ public interface IDiscriminatorConvention /// The discriminator value. BsonValue GetDiscriminator(Type nominalType, Type actualType); } + + //DOMAIN-API These methods should be substitute the above methods in the interface + internal interface IDiscriminatorConventionInternal : IDiscriminatorConvention + { + /// + /// //TODO + /// + /// + /// + /// + /// + Type GetActualType(IBsonReader bsonReader, Type nominalType, IBsonSerializationDomain domain); + + /// + /// //TODO + /// + /// + /// + /// + /// + BsonValue GetDiscriminator(Type nominalType, Type actualType, IBsonSerializationDomain domain); + } } diff --git a/src/MongoDB.Bson/Serialization/Conventions/IMemberMapConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/IMemberMapConvention.cs index b05f40d64ad..dfb138ea4d2 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/IMemberMapConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/IMemberMapConvention.cs @@ -26,4 +26,14 @@ public interface IMemberMapConvention : IConvention /// The member map. void Apply(BsonMemberMap memberMap); } + + internal interface IMemberMapConventionInternal : IMemberMapConvention + { + /// + /// //TODO + /// + /// + /// + void Apply(BsonMemberMap memberMap, IBsonSerializationDomain domain); + } } diff --git a/src/MongoDB.Bson/Serialization/Conventions/IPostProcessingConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/IPostProcessingConvention.cs index ddb7a095df5..ea43f926c4a 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/IPostProcessingConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/IPostProcessingConvention.cs @@ -26,4 +26,14 @@ public interface IPostProcessingConvention : IConvention /// The class map. void PostProcess(BsonClassMap classMap); } + + internal interface IPostProcessingConventionInternal : IPostProcessingConvention + { + /// + /// //TODO + /// + /// + /// + void PostProcess(BsonClassMap classMap, IBsonSerializationDomain domain); + } } diff --git a/src/MongoDB.Bson/Serialization/Conventions/IScalarDiscriminatorConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/IScalarDiscriminatorConvention.cs index 82c2e794626..4d09b977b01 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/IScalarDiscriminatorConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/IScalarDiscriminatorConvention.cs @@ -29,4 +29,9 @@ public interface IScalarDiscriminatorConvention : IDiscriminatorConvention /// The discriminators. BsonValue[] GetDiscriminatorsForTypeAndSubTypes(Type type); } + + internal interface IScalarDiscriminatorConventionInternal : IScalarDiscriminatorConvention + { + BsonValue[] GetDiscriminatorsForTypeAndSubTypes(Type type, IBsonSerializationDomain serializationDomain); + } } diff --git a/src/MongoDB.Bson/Serialization/Conventions/IgnoreIfDefaultConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/IgnoreIfDefaultConvention.cs index aede146fe8c..a7d855cf2e0 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/IgnoreIfDefaultConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/IgnoreIfDefaultConvention.cs @@ -18,7 +18,7 @@ namespace MongoDB.Bson.Serialization.Conventions /// /// A convention that sets whether to ignore default values during serialization. /// - public class IgnoreIfDefaultConvention : ConventionBase, IMemberMapConvention + public class IgnoreIfDefaultConvention : ConventionBase, IMemberMapConventionInternal { // private fields private bool _ignoreIfDefault; @@ -37,7 +37,10 @@ public IgnoreIfDefaultConvention(bool ignoreIfDefault) /// Applies a modification to the member map. /// /// The member map. - public void Apply(BsonMemberMap memberMap) + public void Apply(BsonMemberMap memberMap) => (this as IMemberMapConventionInternal).Apply(memberMap, BsonSerializer.DefaultSerializationDomain); + + /// + void IMemberMapConventionInternal.Apply(BsonMemberMap memberMap, IBsonSerializationDomain domain) { memberMap.SetIgnoreIfDefault(_ignoreIfDefault); } diff --git a/src/MongoDB.Bson/Serialization/Conventions/IgnoreIfNullConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/IgnoreIfNullConvention.cs index c99f694fe81..cebcd8dc86d 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/IgnoreIfNullConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/IgnoreIfNullConvention.cs @@ -18,7 +18,7 @@ namespace MongoDB.Bson.Serialization.Conventions /// /// A convention that sets whether to ignore nulls during serialization. /// - public class IgnoreIfNullConvention : ConventionBase, IMemberMapConvention + public class IgnoreIfNullConvention : ConventionBase, IMemberMapConventionInternal { // private fields private bool _ignoreIfNull; @@ -37,7 +37,10 @@ public IgnoreIfNullConvention(bool ignoreIfNull) /// Applies a modification to the member map. /// /// The member map. - public void Apply(BsonMemberMap memberMap) + public void Apply(BsonMemberMap memberMap) => (this as IMemberMapConventionInternal).Apply(memberMap, BsonSerializer.DefaultSerializationDomain); + + /// + void IMemberMapConventionInternal.Apply(BsonMemberMap memberMap, IBsonSerializationDomain domain) { memberMap.SetIgnoreIfNull(_ignoreIfNull); } diff --git a/src/MongoDB.Bson/Serialization/Conventions/LookupIdGeneratorConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/LookupIdGeneratorConvention.cs index 81f31d14444..19eb4ee2058 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/LookupIdGeneratorConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/LookupIdGeneratorConvention.cs @@ -13,29 +13,26 @@ * limitations under the License. */ -using System; -using System.Reflection; - namespace MongoDB.Bson.Serialization.Conventions { /// /// A convention that looks up an id generator for the id member. /// - public class LookupIdGeneratorConvention : ConventionBase, IPostProcessingConvention + public class LookupIdGeneratorConvention : ConventionBase, IPostProcessingConventionInternal { - // public methods - /// - /// Applies a post processing modification to the class map. - /// - /// The class map. - public void PostProcess(BsonClassMap classMap) + /// + public void PostProcess(BsonClassMap classMap) => (this as IPostProcessingConventionInternal).PostProcess(classMap, BsonSerializer.DefaultSerializationDomain); + + /// + void IPostProcessingConventionInternal.PostProcess(BsonClassMap classMap, IBsonSerializationDomain domain) { var idMemberMap = classMap.IdMemberMap; if (idMemberMap != null) { if (idMemberMap.IdGenerator == null) { - var idGenerator = BsonSerializer.LookupIdGenerator(idMemberMap.MemberType); + //or we pass the domain to the BsonClassMap. The first probably makes more sense, but it's messier. + var idGenerator = domain.LookupIdGenerator(idMemberMap.MemberType); if (idGenerator != null) { idMemberMap.SetIdGenerator(idGenerator); diff --git a/src/MongoDB.Bson/Serialization/Conventions/MemberDefaultValueConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/MemberDefaultValueConvention.cs index 51ff212c300..29076be1b2c 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/MemberDefaultValueConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/MemberDefaultValueConvention.cs @@ -20,7 +20,7 @@ namespace MongoDB.Bson.Serialization.Conventions /// /// A convention that sets the default value for members of a given type. /// - public class MemberDefaultValueConvention : ConventionBase, IMemberMapConvention + public class MemberDefaultValueConvention : ConventionBase, IMemberMapConventionInternal { // private fields private readonly Type _type; @@ -43,7 +43,10 @@ public MemberDefaultValueConvention(Type type, object defaultValue) /// Applies a modification to the member map. /// /// The member map. - public void Apply(BsonMemberMap memberMap) + public void Apply(BsonMemberMap memberMap) => (this as IMemberMapConventionInternal).Apply(memberMap, BsonSerializer.DefaultSerializationDomain); + + /// + void IMemberMapConventionInternal.Apply(BsonMemberMap memberMap, IBsonSerializationDomain domain) { if (memberMap.MemberType == _type) { diff --git a/src/MongoDB.Bson/Serialization/Conventions/MemberNameElementNameConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/MemberNameElementNameConvention.cs index bdee9118a15..daa0a1314a9 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/MemberNameElementNameConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/MemberNameElementNameConvention.cs @@ -13,22 +13,22 @@ * limitations under the License. */ -using System; -using System.Reflection; - namespace MongoDB.Bson.Serialization.Conventions { /// /// A convention that sets the element name the same as the member name. /// - public class MemberNameElementNameConvention : ConventionBase, IMemberMapConvention + public class MemberNameElementNameConvention : ConventionBase, IMemberMapConventionInternal { // public methods /// /// Applies a modification to the member map. /// /// The member map. - public void Apply(BsonMemberMap memberMap) + public void Apply(BsonMemberMap memberMap) => (this as IMemberMapConventionInternal).Apply(memberMap, BsonSerializer.DefaultSerializationDomain); + + /// + void IMemberMapConventionInternal.Apply(BsonMemberMap memberMap, IBsonSerializationDomain domain) { memberMap.SetElementName(memberMap.MemberName); } diff --git a/src/MongoDB.Bson/Serialization/Conventions/NoIdMemberConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/NoIdMemberConvention.cs index 82d295cb42f..4a4b4a829ff 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/NoIdMemberConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/NoIdMemberConvention.cs @@ -13,24 +13,22 @@ * limitations under the License. */ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - namespace MongoDB.Bson.Serialization.Conventions { /// /// A convention that sets a class's IdMember to null. /// - public class NoIdMemberConvention : ConventionBase, IPostProcessingConvention + public class NoIdMemberConvention : ConventionBase, IPostProcessingConventionInternal { // public methods /// /// Applies a post processing modification to the class map. /// /// The class map. - public void PostProcess(BsonClassMap classMap) + public void PostProcess(BsonClassMap classMap) => (this as IPostProcessingConventionInternal).PostProcess(classMap, BsonSerializer.DefaultSerializationDomain); + + /// + void IPostProcessingConventionInternal.PostProcess(BsonClassMap classMap, IBsonSerializationDomain domain) { classMap.SetIdMember(null); } diff --git a/src/MongoDB.Bson/Serialization/Conventions/ObjectDiscriminatorConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/ObjectDiscriminatorConvention.cs index 9a0a7683368..f732a04ea3f 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/ObjectDiscriminatorConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/ObjectDiscriminatorConvention.cs @@ -86,7 +86,11 @@ obj is ObjectDiscriminatorConvention other && /// The reader. /// The nominal type. /// The actual type. - public Type GetActualType(IBsonReader bsonReader, Type nominalType) + public Type GetActualType(IBsonReader bsonReader, Type nominalType) => + GetActualType(bsonReader, nominalType, BsonSerializer.DefaultSerializationDomain); + + /// + internal Type GetActualType(IBsonReader bsonReader, Type nominalType, IBsonSerializationDomain domain) { // the BsonReader is sitting at the value whose actual type needs to be found var bsonType = bsonReader.GetCurrentBsonType(); @@ -129,13 +133,13 @@ public Type GetActualType(IBsonReader bsonReader, Type nominalType) var actualType = nominalType; if (bsonReader.FindElement(_elementName)) { - var context = BsonDeserializationContext.CreateRoot(bsonReader); + var context = BsonDeserializationContext.CreateRoot(bsonReader, domain); var discriminator = BsonValueSerializer.Instance.Deserialize(context); if (discriminator.IsBsonArray) { discriminator = discriminator.AsBsonArray.Last(); // last item is leaf class discriminator } - actualType = BsonSerializer.LookupActualType(nominalType, discriminator); + actualType = domain.LookupActualType(nominalType, discriminator); } bsonReader.ReturnToBookmark(bookmark); return actualType; diff --git a/src/MongoDB.Bson/Serialization/Conventions/ObjectSerializerAllowedTypesConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/ObjectSerializerAllowedTypesConvention.cs index f4860f81c9f..5da3436d025 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/ObjectSerializerAllowedTypesConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/ObjectSerializerAllowedTypesConvention.cs @@ -25,7 +25,7 @@ namespace MongoDB.Bson.Serialization.Conventions /// /// A convention that allows to set the types that can be safely serialized and deserialized with the . /// - public sealed class ObjectSerializerAllowedTypesConvention : ConventionBase, IMemberMapConvention + public sealed class ObjectSerializerAllowedTypesConvention : ConventionBase, IMemberMapConventionInternal { // static properties @@ -151,7 +151,10 @@ public bool AllowDefaultFrameworkTypes /// Applies a modification to the member map. /// /// The member map. - public void Apply(BsonMemberMap memberMap) + public void Apply(BsonMemberMap memberMap) => (this as IMemberMapConventionInternal).Apply(memberMap, BsonSerializer.DefaultSerializationDomain); + + /// + void IMemberMapConventionInternal.Apply(BsonMemberMap memberMap, IBsonSerializationDomain domain) { var memberType = memberMap.MemberType; diff --git a/src/MongoDB.Bson/Serialization/Conventions/ResetMemberMapsConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/ResetMemberMapsConvention.cs index ef21036c3e3..959aecc0900 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/ResetMemberMapsConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/ResetMemberMapsConvention.cs @@ -18,14 +18,17 @@ namespace MongoDB.Bson.Serialization.Conventions /// /// A convention that resets class members (resetting any changes that earlier conventions may have applied). /// - public class ResetMemberMapsConvention : ConventionBase, IMemberMapConvention + public class ResetMemberMapsConvention : ConventionBase, IMemberMapConventionInternal { // public methods /// /// Applies a modification to the member map. /// /// The member map. - public void Apply(BsonMemberMap memberMap) + public void Apply(BsonMemberMap memberMap) => (this as IMemberMapConventionInternal).Apply(memberMap, BsonSerializer.DefaultSerializationDomain); + + /// + void IMemberMapConventionInternal.Apply(BsonMemberMap memberMap, IBsonSerializationDomain domain) { memberMap.Reset(); } diff --git a/src/MongoDB.Bson/Serialization/Conventions/ScalarDiscriminatorConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/ScalarDiscriminatorConvention.cs index 521aff9b1f7..d9259824a24 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/ScalarDiscriminatorConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/ScalarDiscriminatorConvention.cs @@ -15,13 +15,14 @@ using System; using System.Collections.Concurrent; +using MongoDB.Bson.IO; namespace MongoDB.Bson.Serialization.Conventions { /// /// Represents a discriminator convention where the discriminator is provided by the class map of the actual type. /// - public class ScalarDiscriminatorConvention : StandardDiscriminatorConvention, IScalarDiscriminatorConvention + public class ScalarDiscriminatorConvention : StandardDiscriminatorConvention, IScalarDiscriminatorConventionInternal, IDiscriminatorConventionInternal { private readonly ConcurrentDictionary _cachedTypeAndSubTypeDiscriminators = new(); @@ -35,6 +36,11 @@ public ScalarDiscriminatorConvention(string elementName) { } + Type IDiscriminatorConventionInternal.GetActualType(IBsonReader bsonReader, Type nominalType, IBsonSerializationDomain domain) + { + return base.GetActualType(bsonReader, nominalType, domain); + } + // public methods /// /// Gets the discriminator value for an actual type. @@ -42,10 +48,14 @@ public ScalarDiscriminatorConvention(string elementName) /// The nominal type. /// The actual type. /// The discriminator value. - public override BsonValue GetDiscriminator(Type nominalType, Type actualType) + public override BsonValue GetDiscriminator(Type nominalType, Type actualType) => + (this as IDiscriminatorConventionInternal).GetDiscriminator(nominalType, actualType, BsonSerializer.DefaultSerializationDomain); + + /// + BsonValue IDiscriminatorConventionInternal.GetDiscriminator(Type nominalType, Type actualType, IBsonSerializationDomain domain) { // TODO: this isn't quite right, not all classes are serialized using a class map serializer - var classMap = BsonClassMap.LookupClassMap(actualType); + var classMap = domain.BsonClassMap.LookupClassMap(actualType); if (actualType != nominalType || classMap.DiscriminatorIsRequired) { return classMap.Discriminator; @@ -57,9 +67,12 @@ public override BsonValue GetDiscriminator(Type nominalType, Type actualType) } /// - public BsonValue[] GetDiscriminatorsForTypeAndSubTypes(Type type) + public BsonValue[] GetDiscriminatorsForTypeAndSubTypes(Type type) => + (this as IScalarDiscriminatorConventionInternal).GetDiscriminatorsForTypeAndSubTypes(type, BsonSerializer.DefaultSerializationDomain); + + BsonValue[] IScalarDiscriminatorConventionInternal.GetDiscriminatorsForTypeAndSubTypes(Type type, IBsonSerializationDomain serializationDomain) { - return _cachedTypeAndSubTypeDiscriminators.GetOrAdd(type, BsonSerializer.GetDiscriminatorsForTypeAndSubTypes); + return _cachedTypeAndSubTypeDiscriminators.GetOrAdd(type, serializationDomain.GetDiscriminatorsForTypeAndSubTypes); } } } diff --git a/src/MongoDB.Bson/Serialization/Conventions/StandardDiscriminatorConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/StandardDiscriminatorConvention.cs index d2b042d1fc0..e215da6d68c 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/StandardDiscriminatorConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/StandardDiscriminatorConvention.cs @@ -95,30 +95,33 @@ obj is StandardDiscriminatorConvention other && /// The reader. /// The nominal type. /// The actual type. - public Type GetActualType(IBsonReader bsonReader, Type nominalType) + public Type GetActualType(IBsonReader bsonReader, Type nominalType) => //TODO This one should not be used + GetActualType(bsonReader, nominalType, BsonSerializer.DefaultSerializationDomain); + + internal Type GetActualType(IBsonReader bsonReader, Type nominalType, IBsonSerializationDomain domain) { // the BsonReader is sitting at the value whose actual type needs to be found var bsonType = bsonReader.GetCurrentBsonType(); if (bsonType == BsonType.Document) { // ensure KnownTypes of nominalType are registered (so IsTypeDiscriminated returns correct answer) - BsonSerializer.EnsureKnownTypesAreRegistered(nominalType); + domain.EnsureKnownTypesAreRegistered(nominalType); // we can skip looking for a discriminator if nominalType has no discriminated sub types - if (BsonSerializer.IsTypeDiscriminated(nominalType)) + if (domain.IsTypeDiscriminated(nominalType)) { var bookmark = bsonReader.GetBookmark(); bsonReader.ReadStartDocument(); var actualType = nominalType; if (bsonReader.FindElement(_elementName)) { - var context = BsonDeserializationContext.CreateRoot(bsonReader); + var context = BsonDeserializationContext.CreateRoot(bsonReader, domain); var discriminator = BsonValueSerializer.Instance.Deserialize(context); if (discriminator.IsBsonArray) { discriminator = discriminator.AsBsonArray.Last(); // last item is leaf class discriminator } - actualType = BsonSerializer.LookupActualType(nominalType, discriminator); + actualType = domain.LookupActualType(nominalType, discriminator); } bsonReader.ReturnToBookmark(bookmark); return actualType; diff --git a/src/MongoDB.Bson/Serialization/Conventions/StringIdStoredAsObjectIdConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/StringIdStoredAsObjectIdConvention.cs index 923936d1508..d757e33b6f4 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/StringIdStoredAsObjectIdConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/StringIdStoredAsObjectIdConvention.cs @@ -23,10 +23,13 @@ namespace MongoDB.Bson.Serialization.Conventions /// This convention is only responsible for setting the serializer and idGenerator. It is assumed that this convention runs after /// other conventions that identify which member is the _id and that the _id has already been added to the class map. /// - public class StringIdStoredAsObjectIdConvention : ConventionBase, IMemberMapConvention + public class StringIdStoredAsObjectIdConvention : ConventionBase, IMemberMapConventionInternal { /// - public void Apply(BsonMemberMap memberMap) + public void Apply(BsonMemberMap memberMap) => (this as IMemberMapConventionInternal).Apply(memberMap, BsonSerializer.DefaultSerializationDomain); + + /// + void IMemberMapConventionInternal.Apply(BsonMemberMap memberMap, IBsonSerializationDomain domain) { if (memberMap != memberMap.ClassMap.IdMemberMap) { @@ -38,7 +41,7 @@ public void Apply(BsonMemberMap memberMap) return; } - var defaultStringSerializer = BsonSerializer.LookupSerializer(typeof(string)); + var defaultStringSerializer = domain.LookupSerializer(typeof(string)); if (memberMap.GetSerializer() != defaultStringSerializer) { return; diff --git a/src/MongoDB.Bson/Serialization/Conventions/StringObjectIdIdGeneratorConvention.cs b/src/MongoDB.Bson/Serialization/Conventions/StringObjectIdIdGeneratorConvention.cs index 618d7213aec..0a8f48cf3db 100644 --- a/src/MongoDB.Bson/Serialization/Conventions/StringObjectIdIdGeneratorConvention.cs +++ b/src/MongoDB.Bson/Serialization/Conventions/StringObjectIdIdGeneratorConvention.cs @@ -14,7 +14,6 @@ */ using MongoDB.Bson.Serialization.IdGenerators; -using MongoDB.Bson.Serialization.Options; using MongoDB.Bson.Serialization.Serializers; namespace MongoDB.Bson.Serialization.Conventions @@ -22,14 +21,17 @@ namespace MongoDB.Bson.Serialization.Conventions /// /// A convention that sets the id generator for a string member with a BSON representation of ObjectId. /// - public class StringObjectIdIdGeneratorConvention : ConventionBase, IPostProcessingConvention + public class StringObjectIdIdGeneratorConvention : ConventionBase, IPostProcessingConventionInternal { // public methods /// /// Applies a post processing modification to the class map. /// /// The class map. - public void PostProcess(BsonClassMap classMap) + public void PostProcess(BsonClassMap classMap) => (this as IPostProcessingConventionInternal).PostProcess(classMap, BsonSerializer.DefaultSerializationDomain); + + /// + void IPostProcessingConventionInternal.PostProcess(BsonClassMap classMap, IBsonSerializationDomain domain) { var idMemberMap = classMap.IdMemberMap; if (idMemberMap != null) diff --git a/src/MongoDB.Bson/Serialization/IBsonClassMapDomain.cs b/src/MongoDB.Bson/Serialization/IBsonClassMapDomain.cs new file mode 100644 index 00000000000..6a0df68e291 --- /dev/null +++ b/src/MongoDB.Bson/Serialization/IBsonClassMapDomain.cs @@ -0,0 +1,96 @@ +/* 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.Reflection; + +namespace MongoDB.Bson.Serialization +{ + internal interface IBsonClassMapDomain + { + /// + /// Gets all registered class maps. + /// + /// All registered class maps. + IEnumerable 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. + bool IsClassMapRegistered(Type type); + + /// + /// Looks up a class map (will AutoMap the class if no class map is registered). + /// + /// The class type. + /// The class map. + BsonClassMap LookupClassMap(Type classType); + + /// + /// Creates and registers a class map. + /// + /// The class. + /// The class map. + BsonClassMap RegisterClassMap(); + + /// + /// Creates and registers a class map. + /// + /// The class. + /// The class map initializer. + /// The class map. + BsonClassMap RegisterClassMap(Action> classMapInitializer); + + /// + /// Registers a class map. + /// + /// The class map. + void RegisterClassMap(BsonClassMap classMap); + + /// + /// 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. + bool TryRegisterClassMap(); + + /// + /// 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. + bool TryRegisterClassMap(BsonClassMap 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. + bool TryRegisterClassMap(Action> 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. + bool TryRegisterClassMap(Func> classMapFactory); + } +} \ No newline at end of file diff --git a/src/MongoDB.Bson/Serialization/IBsonIdProvider.cs b/src/MongoDB.Bson/Serialization/IBsonIdProvider.cs index 6cb9ba4a811..d63d71a0c10 100644 --- a/src/MongoDB.Bson/Serialization/IBsonIdProvider.cs +++ b/src/MongoDB.Bson/Serialization/IBsonIdProvider.cs @@ -39,4 +39,9 @@ public interface IBsonIdProvider /// The Id. void SetDocumentId(object document, object id); } + + internal interface IBsonIdProviderInternal : IBsonIdProvider + { + bool GetDocumentId(object document, IBsonSerializationDomain serializationDomain, out object id, out Type idNominalType, out IIdGenerator idGenerator); + } } diff --git a/src/MongoDB.Bson/Serialization/IBsonSerializationDomain.cs b/src/MongoDB.Bson/Serialization/IBsonSerializationDomain.cs new file mode 100644 index 00000000000..34315129a4d --- /dev/null +++ b/src/MongoDB.Bson/Serialization/IBsonSerializationDomain.cs @@ -0,0 +1,317 @@ +using System; +using System.IO; +using System.Threading; +using MongoDB.Bson.IO; +using MongoDB.Bson.Serialization.Conventions; + +namespace MongoDB.Bson.Serialization +{ + /// + /// //TODO + /// + internal interface IBsonSerializationDomain + { + string Name { get; } //FP This is used for debugging purposes, but we could decide to make it public if needed. + + /// + /// Returns whether the given type has any discriminators registered for any of its subclasses. + /// + /// A Type. + /// True if the type is discriminated. + bool IsTypeDiscriminated(Type type); + + /// + /// Looks up the actual type of an object to be deserialized. + /// + /// The nominal type of the object. + /// The discriminator. + /// The actual type of the object. + Type LookupActualType(Type nominalType, BsonValue discriminator); + + /// + /// Looks up the discriminator convention for a type. + /// + /// The type. + /// A discriminator convention. + IDiscriminatorConvention LookupDiscriminatorConvention(Type type); + + /// + /// Looks up an IdGenerator. + /// + /// The Id type. + /// An IdGenerator for the Id type. + IIdGenerator LookupIdGenerator(Type type); + + /// + /// Looks up a serializer for a Type. + /// + /// The type. + /// A serializer for type T. + IBsonSerializer LookupSerializer(); + + /// + /// Looks up a serializer for a Type. + /// + /// The Type. + /// A serializer for the Type. + IBsonSerializer LookupSerializer(Type type); + + /// + /// Gets the serializer registry. + /// + IBsonSerializerRegistry SerializerRegistry { get; } + + /// + /// Registers the discriminator for a type. + /// + /// The type. + /// The discriminator. + void RegisterDiscriminator(Type type, BsonValue discriminator); + + /// + /// Registers the discriminator convention for a type. + /// + /// Type type. + /// The discriminator convention. + void RegisterDiscriminatorConvention(Type type, IDiscriminatorConvention convention); + + /// + /// Registers a generic serializer definition for a generic type. + /// + /// The generic type. + /// The generic serializer definition. + void RegisterGenericSerializerDefinition( + Type genericTypeDefinition, + Type genericSerializerDefinition); + + /// + /// Registers an IdGenerator for an Id Type. + /// + /// The Id Type. + /// The IdGenerator for the Id Type. + void RegisterIdGenerator(Type type, IIdGenerator idGenerator); + + /// + /// Registers a serialization provider. + /// + /// The serialization provider. + void RegisterSerializationProvider(IBsonSerializationProvider provider); + + /// + /// Registers a serializer for a type. + /// + /// The type. + /// The serializer. + void RegisterSerializer(IBsonSerializer serializer); + + /// + /// Registers a serializer for a type. + /// + /// The type. + /// The serializer. + void RegisterSerializer(Type type, IBsonSerializer serializer); + + /// + /// Tries to register a serializer for a type. + /// + /// The serializer. + /// The type. + /// True if the serializer was registered on this call, false if the same serializer was already registered on a previous call, throws an exception if a different serializer was already registered. + bool TryRegisterSerializer(Type type, IBsonSerializer serializer); + + /// + /// Tries to register a serializer for a type. + /// + /// The type. + /// The serializer. + /// True if the serializer was registered on this call, false if the same serializer was already registered on a previous call, throws an exception if a different serializer was already registered. + bool TryRegisterSerializer(IBsonSerializer serializer); + + /// + /// Gets or sets whether to use the NullIdChecker on reference Id types that don't have an IdGenerator registered. + /// + bool UseNullIdChecker { get; set; } + + /// + /// Gets or sets whether to use the ZeroIdChecker on value Id types that don't have an IdGenerator registered. + /// + bool UseZeroIdChecker { get; set; } + + /// + /// Deserializes an object from a BsonDocument. + /// + /// The nominal type of the object. + /// The BsonDocument. + /// The configurator. + /// A deserialized value. + TNominalType Deserialize(BsonDocument document, + Action configurator = null); + + /// + /// Deserializes a value. + /// + /// The nominal type of the object. + /// The BsonReader. + /// The configurator. + /// A deserialized value. + TNominalType Deserialize(IBsonReader bsonReader, + Action configurator = null); + + /// + /// Deserializes an object from a BSON byte array. + /// + /// The nominal type of the object. + /// The BSON byte array. + /// The configurator. + /// A deserialized value. + TNominalType Deserialize(byte[] bytes, + Action configurator = null); + + /// + /// Deserializes an object from a BSON Stream. + /// + /// The nominal type of the object. + /// The BSON Stream. + /// The configurator. + /// A deserialized value. + TNominalType Deserialize(Stream stream, + Action configurator = null); + + /// + /// Deserializes an object from a JSON string. + /// + /// The nominal type of the object. + /// The JSON string. + /// The configurator. + /// A deserialized value. + TNominalType Deserialize(string json, + Action configurator = null); + + /// + /// Deserializes an object from a JSON TextReader. + /// + /// The nominal type of the object. + /// The JSON TextReader. + /// The configurator. + /// A deserialized value. + TNominalType Deserialize(TextReader textReader, + Action configurator = null); + + /// + /// Deserializes an object from a BsonDocument. + /// + /// The BsonDocument. + /// The nominal type of the object. + /// The configurator. + /// A deserialized value. + object Deserialize(BsonDocument document, Type nominalType, + Action configurator = null); + + /// + /// Deserializes a value. + /// + /// The BsonReader. + /// The nominal type of the object. + /// The configurator. + /// A deserialized value. + object Deserialize(IBsonReader bsonReader, Type nominalType, + Action configurator = null); + + /// + /// Deserializes an object from a BSON byte array. + /// + /// The BSON byte array. + /// The nominal type of the object. + /// The configurator. + /// A deserialized value. + object Deserialize(byte[] bytes, Type nominalType, + Action configurator = null); + + /// + /// Deserializes an object from a BSON Stream. + /// + /// The BSON Stream. + /// The nominal type of the object. + /// The configurator. + /// A deserialized value. + object Deserialize(Stream stream, Type nominalType, + Action configurator = null); + + /// + /// Deserializes an object from a JSON string. + /// + /// The JSON string. + /// The nominal type of the object. + /// The configurator. + /// A deserialized value. + object Deserialize(string json, Type nominalType, + Action configurator = null); + + /// + /// Deserializes an object from a JSON TextReader. + /// + /// The JSON TextReader. + /// The nominal type of the object. + /// The configurator. + /// A deserialized value. + object Deserialize(TextReader textReader, Type nominalType, + Action configurator = null); + + /// + /// Serializes a value. + /// + /// The nominal type of the object. + /// The BsonWriter. + /// The object. + /// The serialization context configurator. + /// The serialization args. + void Serialize( + IBsonWriter bsonWriter, + TNominalType value, + Action configurator = null, + BsonSerializationArgs args = default(BsonSerializationArgs)); + + /// + /// Serializes a value. + /// + /// The BsonWriter. + /// The nominal type of the object. + /// The object. + /// The serialization context configurator. + /// The serialization args. + void Serialize( + IBsonWriter bsonWriter, + Type nominalType, + object value, + Action configurator = null, + BsonSerializationArgs args = default(BsonSerializationArgs)); + + /// + /// //TODO + /// + IBsonClassMapDomain BsonClassMap { get; } + + /// + /// //TODO + /// + IConventionRegistryDomain ConventionRegistry { get; } + + /// + /// //TODO + /// + IBsonDefaults BsonDefaults { get; } + + //DOMAIN-API The following methods and properties were not public on BsonSerializer + + void EnsureKnownTypesAreRegistered(Type nominalType); + + BsonValue[] GetDiscriminatorsForTypeAndSubTypes(Type type); + + IDiscriminatorConvention GetOrRegisterDiscriminatorConvention(Type type, + IDiscriminatorConvention discriminatorConvention); + + bool IsDiscriminatorConventionRegisteredAtThisLevel(Type type); + + ReaderWriterLockSlim ConfigLock { get; } + } +} \ No newline at end of file diff --git a/src/MongoDB.Bson/Serialization/IBsonSerializationProvider.cs b/src/MongoDB.Bson/Serialization/IBsonSerializationProvider.cs index cdfca5d0676..6d1472eccea 100644 --- a/src/MongoDB.Bson/Serialization/IBsonSerializationProvider.cs +++ b/src/MongoDB.Bson/Serialization/IBsonSerializationProvider.cs @@ -17,6 +17,7 @@ namespace MongoDB.Bson.Serialization { + //DOMAIN-API We should remove this interface and merge it with IRegistryAwareBsonSerializationProvider. /// /// An interface implemented by serialization providers. /// @@ -48,4 +49,13 @@ public interface IRegistryAwareBsonSerializationProvider : IBsonSerializationPro /// IBsonSerializer GetSerializer(Type type, IBsonSerializerRegistry serializerRegistry); } + + //DOMAIN-API We should use this interface the default one, and remove the previous two. + internal interface IDomainAwareBsonSerializationProvider : IRegistryAwareBsonSerializationProvider + { + IBsonSerializationDomain SerializationDomain { get; } + + //FP Can't use just GetSerializer because it's already used by the base interface. + IBsonSerializer GetSerializerWithDomain(Type type); + } } diff --git a/src/MongoDB.Bson/Serialization/IBsonSerializerExtensions.cs b/src/MongoDB.Bson/Serialization/IBsonSerializerExtensions.cs index fd5998c93b3..aa11d7bef91 100644 --- a/src/MongoDB.Bson/Serialization/IBsonSerializerExtensions.cs +++ b/src/MongoDB.Bson/Serialization/IBsonSerializerExtensions.cs @@ -13,7 +13,6 @@ * limitations under the License. */ -using System; using MongoDB.Bson.IO; using MongoDB.Bson.Serialization.Conventions; @@ -56,9 +55,18 @@ public static TValue Deserialize(this IBsonSerializer serializer /// The serializer. /// The discriminator convention. public static IDiscriminatorConvention GetDiscriminatorConvention(this IBsonSerializer serializer) => + GetDiscriminatorConvention(serializer, BsonSerializer.DefaultSerializationDomain); + + /// + /// //TODO + /// + /// + /// + /// + internal static IDiscriminatorConvention GetDiscriminatorConvention(this IBsonSerializer serializer, IBsonSerializationDomain serializationDomain) => serializer is IHasDiscriminatorConvention hasDiscriminatorConvention ? hasDiscriminatorConvention.DiscriminatorConvention - : BsonSerializer.LookupDiscriminatorConvention(serializer.ValueType); + : serializationDomain.LookupDiscriminatorConvention(serializer.ValueType); /// /// Serializes a value. @@ -96,7 +104,8 @@ public static BsonValue ToBsonValue(this IBsonSerializer serializer, object valu var document = new BsonDocument(); using (var writer = new BsonDocumentWriter(document)) { - var context = BsonSerializationContext.CreateRoot(writer); + //QUESTION Is it correct we only need a default domain here? + var context = BsonSerializationContext.CreateRoot(writer, BsonSerializer.DefaultSerializationDomain); writer.WriteStartDocument(); writer.WriteName("x"); serializer.Serialize(context, value); @@ -117,7 +126,8 @@ public static BsonValue ToBsonValue(this IBsonSerializer seriali var document = new BsonDocument(); using (var writer = new BsonDocumentWriter(document)) { - var context = BsonSerializationContext.CreateRoot(writer); + //QUESTION Is it correct we only need a default domain here? + var context = BsonSerializationContext.CreateRoot(writer, BsonSerializer.DefaultSerializationDomain); writer.WriteStartDocument(); writer.WriteName("x"); serializer.Serialize(context, value); diff --git a/src/MongoDB.Bson/Serialization/IIdGenerator.cs b/src/MongoDB.Bson/Serialization/IIdGenerator.cs index 5361038d9e3..2ff3c57d46c 100644 --- a/src/MongoDB.Bson/Serialization/IIdGenerator.cs +++ b/src/MongoDB.Bson/Serialization/IIdGenerator.cs @@ -27,6 +27,7 @@ public interface IIdGenerator /// The document. /// An Id. object GenerateId(object container, object document); + /// /// Tests whether an Id is empty. /// diff --git a/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs index fdcd59916ed..74d37b0069d 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs @@ -58,7 +58,7 @@ public BsonClassMapSerializer(BsonClassMap classMap) // public properties /// - public IDiscriminatorConvention DiscriminatorConvention => _classMap.GetDiscriminatorConvention(); + public IDiscriminatorConvention DiscriminatorConvention => _classMap.GetDiscriminatorConvention(); //TODO This should be removed, because we need to have the serialization domain. /// /// Gets a value indicating whether this serializer's discriminator is compatible with the object serializer. @@ -88,15 +88,16 @@ public override TClass Deserialize(BsonDeserializationContext context, BsonDeser return default(TClass); } - var discriminatorConvention = _classMap.GetDiscriminatorConvention(); + var discriminatorConvention = _classMap.GetDiscriminatorConvention(context.SerializationDomain); + + var actualType = discriminatorConvention.GetActualTypeInternal(bsonReader, args.NominalType, context.SerializationDomain); - var actualType = discriminatorConvention.GetActualType(bsonReader, args.NominalType); if (actualType == typeof(TClass)) { return DeserializeClass(context); } - var serializer = BsonSerializer.LookupSerializer(actualType); + var serializer = context.SerializationDomain.LookupSerializer(actualType); return (TClass)serializer.Deserialize(context); } @@ -146,7 +147,7 @@ public TClass DeserializeClass(BsonDeserializationContext context) } } - var discriminatorConvention = _classMap.GetDiscriminatorConvention(); + var discriminatorConvention = _classMap.GetDiscriminatorConvention(context.SerializationDomain); var allMemberMaps = _classMap.AllMemberMaps; var extraElementsMemberMapIndex = _classMap.ExtraElementsMemberMapIndex; var memberMapBitArray = FastMemberMapHelper.GetBitArray(allMemberMaps.Count); @@ -326,6 +327,10 @@ public bool GetDocumentId( out object id, out Type idNominalType, out IIdGenerator idGenerator) + => GetDocumentId(document, BsonSerializer.DefaultSerializationDomain, out id, out idNominalType, out idGenerator); + + internal bool GetDocumentId(object document, IBsonSerializationDomain serializationDomain, out object id, out Type idNominalType, + out IIdGenerator idGenerator) { var idMemberMap = _classMap.IdMemberMap; if (idMemberMap != null) @@ -392,7 +397,7 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati return; } - var serializer = BsonSerializer.LookupSerializer(actualType); + var serializer = context.SerializationDomain.LookupSerializer(actualType); serializer.Serialize(context, args, value); } @@ -566,7 +571,7 @@ private object DeserializeMemberValue(BsonDeserializationContext context, BsonMe { try { - return memberMap.GetSerializer().Deserialize(context); + return memberMap.GetSerializer(context.SerializationDomain).Deserialize(context); } catch (Exception ex) { @@ -637,11 +642,12 @@ private void SerializeExtraElements(BsonSerializationContext context, object obj private void SerializeDiscriminator(BsonSerializationContext context, Type nominalType, object obj) { - var discriminatorConvention = _classMap.GetDiscriminatorConvention(); + var discriminatorConvention = _classMap.GetDiscriminatorConvention(context.SerializationDomain); if (discriminatorConvention != null) { var actualType = obj.GetType(); - var discriminator = discriminatorConvention.GetDiscriminator(nominalType, actualType); + var discriminator = discriminatorConvention.GetDiscriminatorInternal(nominalType, actualType, context.SerializationDomain); + if (discriminator != null) { context.Writer.WriteName(discriminatorConvention.ElementName); @@ -684,7 +690,7 @@ private void SerializeNormalMember(BsonSerializationContext context, object obj, } bsonWriter.WriteName(memberMap.ElementName); - memberMap.GetSerializer().Serialize(context, value); + memberMap.GetSerializer(context.SerializationDomain).Serialize(context, value); } private bool ShouldSerializeDiscriminator(Type nominalType) diff --git a/src/MongoDB.Bson/Serialization/Serializers/BsonDocumentSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/BsonDocumentSerializer.cs index 2ba62a8294c..a3e7417ee06 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/BsonDocumentSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/BsonDocumentSerializer.cs @@ -81,6 +81,10 @@ public bool GetDocumentId( object document, out object id, out Type idNominalType, + out IIdGenerator idGenerator) => GetDocumentId(document, BsonSerializer.DefaultSerializationDomain, out id, + out idNominalType, out idGenerator); + + internal bool GetDocumentId(object document, IBsonSerializationDomain serializationDomain, out object id, out Type idNominalType, out IIdGenerator idGenerator) { var bsonDocument = (BsonDocument)document; @@ -89,7 +93,7 @@ public bool GetDocumentId( if (bsonDocument.TryGetValue("_id", out idBsonValue)) { id = idBsonValue; - idGenerator = BsonSerializer.LookupIdGenerator(id.GetType()); + idGenerator = serializationDomain.LookupIdGenerator(id.GetType()); if (idGenerator == null) { diff --git a/src/MongoDB.Bson/Serialization/Serializers/BsonValueSerializerBase.cs b/src/MongoDB.Bson/Serialization/Serializers/BsonValueSerializerBase.cs index e3a5776870c..0f636ae834b 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/BsonValueSerializerBase.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/BsonValueSerializerBase.cs @@ -70,7 +70,7 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati var actualType = value.GetType(); if (actualType != ValueType && !args.SerializeAsNominalType) { - var serializer = BsonSerializer.LookupSerializer(actualType); + var serializer = context.SerializationDomain.LookupSerializer(actualType); serializer.Serialize(context, value); return; } diff --git a/src/MongoDB.Bson/Serialization/Serializers/ClassSerializerBase.cs b/src/MongoDB.Bson/Serialization/Serializers/ClassSerializerBase.cs index 07723b9cddb..f21cd498ddf 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/ClassSerializerBase.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/ClassSerializerBase.cs @@ -47,7 +47,7 @@ public override TValue Deserialize(BsonDeserializationContext context, BsonDeser } else { - var serializer = BsonSerializer.LookupSerializer(actualType); + var serializer = context.SerializationDomain.LookupSerializer(actualType); return (TValue)serializer.Deserialize(context, args); } } @@ -75,7 +75,7 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati } else { - var serializer = BsonSerializer.LookupSerializer(actualType); + var serializer = context.SerializationDomain.LookupSerializer(actualType); serializer.Serialize(context, value); } } @@ -100,8 +100,8 @@ protected virtual TValue DeserializeValue(BsonDeserializationContext context, Bs /// The actual type. protected virtual Type GetActualType(BsonDeserializationContext context) { - var discriminatorConvention = this.GetDiscriminatorConvention(); - return discriminatorConvention.GetActualType(context.Reader, typeof(TValue)); + var discriminatorConvention = this.GetDiscriminatorConvention(context.SerializationDomain); + return discriminatorConvention.GetActualTypeInternal(context.Reader, typeof(TValue), context.SerializationDomain); } /// diff --git a/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs index 59e6cb42365..e1c60e22b3f 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/DictionaryInterfaceImplementerSerializer.cs @@ -31,6 +31,7 @@ public sealed class DictionaryInterfaceImplementerSerializer : IDictionaryRepresentationConfigurable where TDictionary : class, IDictionary, new() { + //DOMAIN-API This version should be removed in the future. /// /// Initializes a new instance of the class. /// @@ -38,6 +39,7 @@ public DictionaryInterfaceImplementerSerializer() { } + //DOMAIN-API This version should be removed in the future. /// /// Initializes a new instance of the class. /// diff --git a/src/MongoDB.Bson/Serialization/Serializers/DictionarySerializerBase.cs b/src/MongoDB.Bson/Serialization/Serializers/DictionarySerializerBase.cs index 96b708d8aa6..da4be08eae8 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/DictionarySerializerBase.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/DictionarySerializerBase.cs @@ -45,6 +45,7 @@ private static class Flags private readonly IBsonSerializer _valueSerializer; // constructors + //DOMAIN-API This version should be removed in the future. /// /// Initializes a new instance of the class. /// @@ -53,6 +54,7 @@ public DictionarySerializerBase() { } + //DOMAIN-API This version should be removed in the future. /// /// Initializes a new instance of the class. /// @@ -273,7 +275,8 @@ private object DeserializeKeyString(string keyString) var keyDocument = new BsonDocument("k", keyString); using (var keyReader = new BsonDocumentReader(keyDocument)) { - var context = BsonDeserializationContext.CreateRoot(keyReader); + //QUESTION Is it correct we only need a default domain here? + var context = BsonDeserializationContext.CreateRoot(keyReader, BsonSerializer.DefaultSerializationDomain); keyReader.ReadStartDocument(); keyReader.ReadName("k"); var key = _keySerializer.Deserialize(context); @@ -329,7 +332,8 @@ private string SerializeKeyString(object key) var keyDocument = new BsonDocument(); using (var keyWriter = new BsonDocumentWriter(keyDocument)) { - var context = BsonSerializationContext.CreateRoot(keyWriter); + //QUESTION Is it correct we only need a default domain here? + var context = BsonSerializationContext.CreateRoot(keyWriter, BsonSerializer.DefaultSerializationDomain); keyWriter.WriteStartDocument(); keyWriter.WriteName("k"); _keySerializer.Serialize(context, key); @@ -373,6 +377,7 @@ private static class Flags private readonly Lazy> _lazyValueSerializer; // constructors + //DOMAIN-API This version should be removed in the future. /// /// Initializes a new instance of the class. /// @@ -381,6 +386,8 @@ public DictionarySerializerBase() { } + //DOMAIN-API This version should be removed in the future. + //FP Fortunately it seems that all the constructors that should not be used are actually not used in the codebase. /// /// Initializes a new instance of the class. /// @@ -676,7 +683,8 @@ private TKey DeserializeKeyString(string keyString) var keyDocument = new BsonDocument("k", keyString); using (var keyReader = new BsonDocumentReader(keyDocument)) { - var context = BsonDeserializationContext.CreateRoot(keyReader); + //QUESTION Is it correct we only need a default domain here? + var context = BsonDeserializationContext.CreateRoot(keyReader, BsonSerializer.DefaultSerializationDomain); keyReader.ReadStartDocument(); keyReader.ReadName("k"); var key = _lazyKeySerializer.Value.Deserialize(context); @@ -732,7 +740,8 @@ private string SerializeKeyString(TKey key) var keyDocument = new BsonDocument(); using (var keyWriter = new BsonDocumentWriter(keyDocument)) { - var context = BsonSerializationContext.CreateRoot(keyWriter); + //QUESTION Is it correct we only need a default domain here? + var context = BsonSerializationContext.CreateRoot(keyWriter, BsonSerializer.DefaultSerializationDomain); keyWriter.WriteStartDocument(); keyWriter.WriteName("k"); _lazyKeySerializer.Value.Serialize(context, key); diff --git a/src/MongoDB.Bson/Serialization/Serializers/DiscriminatedInterfaceSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/DiscriminatedInterfaceSerializer.cs index eb0035489e0..36719d18b57 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/DiscriminatedInterfaceSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/DiscriminatedInterfaceSerializer.cs @@ -41,14 +41,14 @@ public sealed class DiscriminatedInterfaceSerializer : // where TInterface is an interface { #region static - private static IBsonSerializer CreateInterfaceSerializer() + private static IBsonSerializer CreateInterfaceSerializer(IBsonSerializationDomain serializationDomain) { var classMapDefinition = typeof(BsonClassMap<>); var classMapType = classMapDefinition.MakeGenericType(typeof(TInterface)); var classMap = (BsonClassMap)Activator.CreateInstance(classMapType); classMap.AutoMap(); - classMap.SetDiscriminatorConvention(BsonSerializer.LookupDiscriminatorConvention(typeof(TInterface))); - classMap.Freeze(); + classMap.SetDiscriminatorConvention(serializationDomain.LookupDiscriminatorConvention(typeof(TInterface))); + classMap.Freeze(serializationDomain); return new BsonClassMapSerializer(classMap); } #endregion @@ -75,7 +75,7 @@ public DiscriminatedInterfaceSerializer() /// interfaceType /// interfaceType public DiscriminatedInterfaceSerializer(IDiscriminatorConvention discriminatorConvention) - : this(discriminatorConvention, CreateInterfaceSerializer(), objectSerializer: null) + : this(discriminatorConvention, CreateInterfaceSerializer(BsonSerializer.DefaultSerializationDomain), objectSerializer: null) //TODO Is this ok? { } @@ -109,12 +109,12 @@ public DiscriminatedInterfaceSerializer(IDiscriminatorConvention discriminatorCo } _interfaceType = typeof(TInterface); - _discriminatorConvention = discriminatorConvention ?? interfaceSerializer.GetDiscriminatorConvention(); + _discriminatorConvention = discriminatorConvention ?? interfaceSerializer.GetDiscriminatorConvention(); //QUESTION What do we do here? We don't have the domain close by, should we lazy initialize it during serialization/deserialization? _interfaceSerializer = interfaceSerializer; if (objectSerializer == null) { - objectSerializer = BsonSerializer.LookupSerializer(); + objectSerializer = BsonSerializer.LookupSerializer(); //QUESTION What do we do here? We don't have the domain close by, should we lazy initialize it during serialization/deserialization? if (objectSerializer is ObjectSerializer standardObjectSerializer) { Func allowedTypes = (Type type) => typeof(TInterface).IsAssignableFrom(type); @@ -158,7 +158,7 @@ public override TInterface Deserialize(BsonDeserializationContext context, BsonD } else { - var actualType = _discriminatorConvention.GetActualType(bsonReader, typeof(TInterface)); + var actualType = _discriminatorConvention.GetActualTypeInternal(bsonReader, typeof(TInterface), context.SerializationDomain); if (actualType == _interfaceType) { var message = string.Format("Unable to determine actual type of object to deserialize for interface type {0}.", _interfaceType.FullName); diff --git a/src/MongoDB.Bson/Serialization/Serializers/DiscriminatedWrapperSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/DiscriminatedWrapperSerializer.cs index d7d5be76596..b85342a1f46 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/DiscriminatedWrapperSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/DiscriminatedWrapperSerializer.cs @@ -73,8 +73,8 @@ public override TValue Deserialize(BsonDeserializationContext context, BsonDeser { var bsonReader = context.Reader; var nominalType = args.NominalType; - var actualType = _discriminatorConvention.GetActualType(bsonReader, nominalType); - var serializer = BsonSerializer.LookupSerializer(actualType); + var actualType = _discriminatorConvention.GetActualTypeInternal(bsonReader, nominalType, context.SerializationDomain); + var serializer = context.SerializationDomain.LookupSerializer(actualType); TValue value = default(TValue); _helper.DeserializeMembers(context, (elementName, flag) => @@ -145,7 +145,7 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati var bsonWriter = context.Writer; var nominalType = args.NominalType; var actualType = value.GetType(); - var discriminator = _discriminatorConvention.GetDiscriminator(nominalType, actualType); + var discriminator = _discriminatorConvention.GetDiscriminatorInternal(nominalType, actualType, context.SerializationDomain); bsonWriter.WriteStartDocument(); if (discriminator != null) diff --git a/src/MongoDB.Bson/Serialization/Serializers/DynamicDocumentBaseSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/DynamicDocumentBaseSerializer.cs index d020fd3895d..1a283d437bf 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/DynamicDocumentBaseSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/DynamicDocumentBaseSerializer.cs @@ -26,8 +26,8 @@ namespace MongoDB.Bson.Serialization.Serializers /// The dynamic type. public abstract class DynamicDocumentBaseSerializer : SerializerBase where T : class, IDynamicMetaObjectProvider { - // private static fields - private static readonly IBsonSerializer __objectSerializer = BsonSerializer.LookupSerializer(); + // private fields + private IBsonSerializer _objectSerializer; // constructors /// @@ -58,7 +58,7 @@ public override T Deserialize(BsonDeserializationContext context, BsonDeserializ while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) { var name = bsonReader.ReadName(); - var value = __objectSerializer.Deserialize(dynamicContext); + var value = GetObjectSerializer(context.SerializationDomain).Deserialize(dynamicContext); SetValueForMember(document, name, value); } bsonReader.ReadEndDocument(); @@ -101,7 +101,7 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati if (TryGetValueForMember(value, memberName, out memberValue)) { bsonWriter.WriteName(memberName); - __objectSerializer.Serialize(dynamicContext, memberValue); + GetObjectSerializer(context.SerializationDomain).Serialize(dynamicContext, memberValue); } } bsonWriter.WriteEndDocument(); @@ -142,5 +142,11 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati /// The value. /// true if the member should be serialized; otherwise false. protected abstract bool TryGetValueForMember(T document, string memberName, out object value); + + //private methods + private IBsonSerializer GetObjectSerializer(IBsonSerializationDomain domain) + { + return _objectSerializer ??= domain.LookupSerializer(); + } } } diff --git a/src/MongoDB.Bson/Serialization/Serializers/ElementAppendingSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/ElementAppendingSerializer.cs index 168a757ee98..5fac80165e6 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/ElementAppendingSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/ElementAppendingSerializer.cs @@ -89,7 +89,7 @@ public void Serialize(BsonSerializationContext context, BsonSerializationArgs ar { var writer = context.Writer; var elementAppendingWriter = new ElementAppendingBsonWriter(writer, _elements, _writerSettingsConfigurator); - var elementAppendingContext = BsonSerializationContext.CreateRoot(elementAppendingWriter, builder => ConfigureElementAppendingContext(builder, context)); + var elementAppendingContext = BsonSerializationContext.CreateRoot(elementAppendingWriter, context.SerializationDomain, builder => ConfigureElementAppendingContext(builder, context)); _documentSerializer.Serialize(elementAppendingContext, args, value); } diff --git a/src/MongoDB.Bson/Serialization/Serializers/EnumerableSerializerBase.cs b/src/MongoDB.Bson/Serialization/Serializers/EnumerableSerializerBase.cs index 5d810b01861..6fd4130815f 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/EnumerableSerializerBase.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/EnumerableSerializerBase.cs @@ -31,6 +31,7 @@ public abstract class EnumerableSerializerBase : SerializerBase, private readonly Lazy _lazyItemSerializer; // constructors + //DOMAIN-API This version should be removed in the future. /// /// Initializes a new instance of the class. /// @@ -230,6 +231,7 @@ public abstract class EnumerableSerializerBase : SerializerBase> _lazyItemSerializer; // constructors + //DOMAIN-API This version should be removed in the future. /// /// Initializes a new instance of the class. /// diff --git a/src/MongoDB.Bson/Serialization/Serializers/ExpandoObjectSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/ExpandoObjectSerializer.cs index 4ac504304e5..f153c47f98f 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/ExpandoObjectSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/ExpandoObjectSerializer.cs @@ -31,12 +31,18 @@ public sealed class ExpandoObjectSerializer : DynamicDocumentBaseSerializer> _listSerializer; + //DOMAIN-API This version should be removed in the future. /// /// Initializes a new instance of the class. /// public ExpandoObjectSerializer() + :this(BsonSerializer.DefaultSerializationDomain.SerializerRegistry) { - _listSerializer = BsonSerializer.LookupSerializer>(); + } + + internal ExpandoObjectSerializer(IBsonSerializerRegistry serializerRegistry) + { + _listSerializer = serializerRegistry.GetSerializer>(); } /// diff --git a/src/MongoDB.Bson/Serialization/Serializers/IEnumerableDeserializingAsCollectionSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/IEnumerableDeserializingAsCollectionSerializer.cs index ca9ec6a771b..8009b37a0d1 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/IEnumerableDeserializingAsCollectionSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/IEnumerableDeserializingAsCollectionSerializer.cs @@ -45,6 +45,7 @@ private static void EnsureTIEnumerableIsAnInterface() // private fields private readonly Lazy> _lazyItemSerializer; + //DOMAIN-API This should be removed in the future. // constructors /// /// Initializes a new instance of the IEnumerableDeserializingAsCollectionSerializer class. diff --git a/src/MongoDB.Bson/Serialization/Serializers/ImpliedImplementationInterfaceSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/ImpliedImplementationInterfaceSerializer.cs index fc8cdf81829..e8cd662d937 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/ImpliedImplementationInterfaceSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/ImpliedImplementationInterfaceSerializer.cs @@ -48,6 +48,7 @@ public sealed class ImpliedImplementationInterfaceSerializer> _lazyImplementationSerializer; // constructors + //DOMAIN-API This should be removed in the future. /// /// Initializes a new instance of the class. /// @@ -279,7 +280,7 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati } else { - var serializer = BsonSerializer.LookupSerializer(actualType); + var serializer = context.SerializationDomain.LookupSerializer(actualType); serializer.Serialize(context, value); } } diff --git a/src/MongoDB.Bson/Serialization/Serializers/KeyValuePairSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/KeyValuePairSerializer.cs index ea1f59ac971..dc084978b70 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/KeyValuePairSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/KeyValuePairSerializer.cs @@ -53,6 +53,7 @@ private static class Flags private readonly Lazy> _lazyValueSerializer; // constructors + //DOMAIN-API This should be removed in the future. /// /// Initializes a new instance of the class. /// @@ -61,6 +62,7 @@ public KeyValuePairSerializer() { } + //DOMAIN-API This should be removed in the future. /// /// Initializes a new instance of the class. /// diff --git a/src/MongoDB.Bson/Serialization/Serializers/NullableSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/NullableSerializer.cs index 8740bdd3a9b..fafceb0d91b 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/NullableSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/NullableSerializer.cs @@ -60,6 +60,7 @@ public sealed class NullableSerializer : private Lazy> _lazySerializer; // constructors + //DOMAIN-API This should be removed in the future. /// /// Initializes a new instance of the class. /// diff --git a/src/MongoDB.Bson/Serialization/Serializers/ObjectSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/ObjectSerializer.cs index 36e4403718c..e3b9dc16c7a 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/ObjectSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/ObjectSerializer.cs @@ -47,7 +47,7 @@ public sealed class ObjectSerializer : ClassSerializerBase, IHasDiscrimi /// /// Gets the standard instance. /// - public static ObjectSerializer Instance => __instance; + public static ObjectSerializer Instance => __instance; //FP This is problematic, but can't do much about it now. /// /// An allowed types function that returns false for all types. @@ -63,6 +63,7 @@ public sealed class ObjectSerializer : ClassSerializerBase, IHasDiscrimi private readonly GuidSerializer _guidSerializer; // constructors + //DOMAIN-API This should be removed in the future. /// /// Initializes a new instance of the class. /// @@ -91,6 +92,7 @@ public ObjectSerializer(IDiscriminatorConvention discriminatorConvention, GuidRe { } + //DOMAIN-API This should be removed in the future. /// /// Initializes a new instance of the class. /// @@ -364,7 +366,8 @@ private object DeserializeDiscriminatedValue(BsonDeserializationContext context, { var bsonReader = context.Reader; - var actualType = _discriminatorConvention.GetActualType(bsonReader, typeof(object)); + var actualType = _discriminatorConvention.GetActualTypeInternal(bsonReader, args.NominalType, context.SerializationDomain); + if (!_allowedDeserializationTypes(actualType)) { throw new BsonSerializationException($"Type {actualType.FullName} is not configured as a type that is allowed to be deserialized for this instance of ObjectSerializer."); @@ -389,7 +392,7 @@ private object DeserializeDiscriminatedValue(BsonDeserializationContext context, } else { - var serializer = BsonSerializer.LookupSerializer(actualType); + var serializer = context.SerializationDomain.LookupSerializer(actualType); var polymorphicSerializer = serializer as IBsonPolymorphicSerializer; if (polymorphicSerializer != null && polymorphicSerializer.IsDiscriminatorCompatibleWithObjectSerializer) { @@ -438,7 +441,7 @@ private void SerializeDiscriminatedValue(BsonSerializationContext context, BsonS throw new BsonSerializationException($"Type {actualType.FullName} is not configured as a type that is allowed to be serialized for this instance of ObjectSerializer."); } - var serializer = BsonSerializer.LookupSerializer(actualType); + var serializer = context.SerializationDomain.LookupSerializer(actualType); var polymorphicSerializer = serializer as IBsonPolymorphicSerializer; if (polymorphicSerializer != null && polymorphicSerializer.IsDiscriminatorCompatibleWithObjectSerializer) @@ -455,7 +458,7 @@ private void SerializeDiscriminatedValue(BsonSerializationContext context, BsonS else { var bsonWriter = context.Writer; - var discriminator = _discriminatorConvention.GetDiscriminator(typeof(object), actualType); + var discriminator = _discriminatorConvention.GetDiscriminatorInternal(typeof(object), actualType, context.SerializationDomain); bsonWriter.WriteStartDocument(); if (discriminator != null) diff --git a/src/MongoDB.Bson/Serialization/Serializers/SerializeAsNominalTypeSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/SerializeAsNominalTypeSerializer.cs index 065ae3873f9..fd17ba37426 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/SerializeAsNominalTypeSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/SerializeAsNominalTypeSerializer.cs @@ -28,6 +28,7 @@ public sealed class SerializeAsNominalTypeSerializer private readonly Lazy> _lazyNominalTypeSerializer; // constructors + //DOMAIN-API This should be removed in the future. /// /// Initializes a new instance of the class. /// diff --git a/src/MongoDB.Bson/Serialization/Serializers/ThreeDimensionalArraySerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/ThreeDimensionalArraySerializer.cs index c2aaf3998fb..02ed938633f 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/ThreeDimensionalArraySerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/ThreeDimensionalArraySerializer.cs @@ -30,6 +30,7 @@ public sealed class ThreeDimensionalArraySerializer : private readonly Lazy> _lazyItemSerializer; // constructors + //DOMAIN-API This should be removed in the future. /// /// Initializes a new instance of the class. /// diff --git a/src/MongoDB.Bson/Serialization/Serializers/TupleSerializers.cs b/src/MongoDB.Bson/Serialization/Serializers/TupleSerializers.cs index a5b1e5f290a..140f40c9259 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/TupleSerializers.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/TupleSerializers.cs @@ -114,7 +114,7 @@ public sealed class TupleSerializer : SealedClassSerializerBase>, /// Initializes a new instance of the class. /// public TupleSerializer() - : this(BsonSerializer.SerializerRegistry) + : this(BsonSerializer.SerializerRegistry) //TODO We can keep this as is { } @@ -208,7 +208,7 @@ public sealed class TupleSerializer : SealedClassSerializerBase class. /// public TupleSerializer() - : this(BsonSerializer.SerializerRegistry) + : this(BsonSerializer.SerializerRegistry) //TODO We can keep this as is { } @@ -318,7 +318,7 @@ public sealed class TupleSerializer : SealedClassSerializerBase class. /// public TupleSerializer() - : this(BsonSerializer.SerializerRegistry) + : this(BsonSerializer.SerializerRegistry) //TODO We can keep this as is { } @@ -443,7 +443,7 @@ public sealed class TupleSerializer : SealedClassSerializerBase< /// Initializes a new instance of the class. /// public TupleSerializer() - : this(BsonSerializer.SerializerRegistry) + : this(BsonSerializer.SerializerRegistry) //TODO We can keep this as is { } @@ -585,7 +585,7 @@ public sealed class TupleSerializer : SealedClassSerializerB /// Initializes a new instance of the class. /// public TupleSerializer() - : this(BsonSerializer.SerializerRegistry) + : this(BsonSerializer.SerializerRegistry) //TODO We can keep this as is { } @@ -743,7 +743,7 @@ public sealed class TupleSerializer : SealedClassSeriali /// Initializes a new instance of the class. /// public TupleSerializer() - : this(BsonSerializer.SerializerRegistry) + : this(BsonSerializer.SerializerRegistry) //TODO We can keep this as is { } @@ -917,7 +917,7 @@ public sealed class TupleSerializer : SealedClassSer /// Initializes a new instance of the class. /// public TupleSerializer() - : this(BsonSerializer.SerializerRegistry) + : this(BsonSerializer.SerializerRegistry) //TODO We can keep this as is { } @@ -1107,7 +1107,7 @@ public sealed class TupleSerializer : SealedC /// Initializes a new instance of the class. /// public TupleSerializer() - : this(BsonSerializer.SerializerRegistry) + : this(BsonSerializer.SerializerRegistry) //TODO We can keep this as is { } diff --git a/src/MongoDB.Bson/Serialization/Serializers/TwoDimensionalArraySerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/TwoDimensionalArraySerializer.cs index afe701dd1c3..db1dc30d5e8 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/TwoDimensionalArraySerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/TwoDimensionalArraySerializer.cs @@ -30,6 +30,7 @@ public sealed class TwoDimensionalArraySerializer : private readonly Lazy> _lazyItemSerializer; // constructors + //DOMAIN-API This should be removed in the future. /// /// Initializes a new instance of the class. /// diff --git a/src/MongoDB.Bson/Serialization/Serializers/UndiscriminatedActualTypeSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/UndiscriminatedActualTypeSerializer.cs index 1587a979163..240ac575b1b 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/UndiscriminatedActualTypeSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/UndiscriminatedActualTypeSerializer.cs @@ -62,7 +62,7 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati else { var actualType = value.GetType(); - var serializer = BsonSerializer.LookupSerializer(actualType); + var serializer = context.SerializationDomain.LookupSerializer(actualType); args.NominalType = actualType; serializer.Serialize(context, args, value); } diff --git a/src/MongoDB.Bson/Serialization/Serializers/ValueTupleSerializers.cs b/src/MongoDB.Bson/Serialization/Serializers/ValueTupleSerializers.cs index d0f52484e58..db9cc0ae18f 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/ValueTupleSerializers.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/ValueTupleSerializers.cs @@ -71,6 +71,7 @@ public sealed class ValueTupleSerializer : StructSerializerBase> _lazyItem1Serializer; // constructors + //DOMAIN-API This should be removed in the future (also for the other versions) /// /// Initializes a new instance of the class. /// diff --git a/src/MongoDB.Driver.Authentication.AWS/SaslSteps/AWSLastSaslStep.cs b/src/MongoDB.Driver.Authentication.AWS/SaslSteps/AWSLastSaslStep.cs index 7d81604edf6..8a73478fd19 100644 --- a/src/MongoDB.Driver.Authentication.AWS/SaslSteps/AWSLastSaslStep.cs +++ b/src/MongoDB.Driver.Authentication.AWS/SaslSteps/AWSLastSaslStep.cs @@ -82,7 +82,7 @@ private byte[] PreparePayload(AWSCredentials credentials, byte[] serverNonce, st private void ParseServerResponse(SaslConversation conversation, byte[] bytesReceivedFromServer, out byte[] serverNonce, out string host) { - var serverFirstMessageDocument = BsonSerializer.Deserialize(bytesReceivedFromServer); + var serverFirstMessageDocument = BsonSerializer.Deserialize(bytesReceivedFromServer); //FP I think this is fine, as it should be default configuration. if (serverFirstMessageDocument.Names.Any(n => !__serverResponseExpectedNames.Contains(n))) { var unexpectedNames = serverFirstMessageDocument.Names.Except(__serverResponseExpectedNames); diff --git a/src/MongoDB.Driver.Encryption/ExplicitEncryptionLibMongoCryptController.cs b/src/MongoDB.Driver.Encryption/ExplicitEncryptionLibMongoCryptController.cs index 657a4cfa87b..cc268c97d4c 100644 --- a/src/MongoDB.Driver.Encryption/ExplicitEncryptionLibMongoCryptController.cs +++ b/src/MongoDB.Driver.Encryption/ExplicitEncryptionLibMongoCryptController.cs @@ -102,7 +102,7 @@ public Guid CreateDataKey( { var wrappedKeyBytes = ProcessStates(context, _keyVaultNamespace.DatabaseNamespace.DatabaseName, cancellationToken); - var wrappedKeyDocument = BsonSerializer.Deserialize(wrappedKeyBytes); + var wrappedKeyDocument = BsonSerializer.Deserialize(wrappedKeyBytes); //FP I think this is fine, as it should be default configuration. var keyId = UnwrapKeyId(wrappedKeyDocument); _keyVaultCollection.Value.InsertOne(wrappedKeyDocument, cancellationToken: cancellationToken); @@ -131,7 +131,7 @@ public async Task CreateDataKeyAsync( { var wrappedKeyBytes = await ProcessStatesAsync(context, _keyVaultNamespace.DatabaseNamespace.DatabaseName, cancellationToken).ConfigureAwait(false); - var wrappedKeyDocument = BsonSerializer.Deserialize(wrappedKeyBytes); + var wrappedKeyDocument = BsonSerializer.Deserialize(wrappedKeyBytes); //FP I think this is fine, as it should be default configuration. var keyId = UnwrapKeyId(wrappedKeyDocument); await _keyVaultCollection.Value.InsertOneAsync(wrappedKeyDocument, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -553,7 +553,7 @@ private byte[] GetWrappedValueBytes(BsonValue value) private static BsonValue RenderFilter(FilterDefinition filter) { - var registry = BsonSerializer.SerializerRegistry; + var registry = BsonSerializer.SerializerRegistry; //FP I think this is fine, as it should be default configuration. var serializer = registry.GetSerializer(); return filter.Render(new(serializer, registry)); } @@ -570,7 +570,7 @@ private static Guid UnwrapKeyId(BsonDocument wrappedKeyDocument) private static BsonValue UnwrapValue(byte[] encryptedWrappedBytes) { - var bsonDocument = BsonSerializer.Deserialize(encryptedWrappedBytes); + var bsonDocument = BsonSerializer.Deserialize(encryptedWrappedBytes); //FP I think this is fine, as it should be default configuration. return bsonDocument["v"]; } } diff --git a/src/MongoDB.Driver/AggregateExpressionDefinition.cs b/src/MongoDB.Driver/AggregateExpressionDefinition.cs index 85ae3f37e6a..eb898f917f0 100644 --- a/src/MongoDB.Driver/AggregateExpressionDefinition.cs +++ b/src/MongoDB.Driver/AggregateExpressionDefinition.cs @@ -140,7 +140,7 @@ internal ExpressionAggregateExpressionDefinition( public override BsonValue Render(RenderArgs args) { var contextData = _contextData?.With("SerializerRegistry", args.SerializerRegistry); - return LinqProviderAdapter.TranslateExpressionToAggregateExpression(_expression, args.DocumentSerializer, args.SerializerRegistry, args.TranslationOptions, contextData); + return LinqProviderAdapter.TranslateExpressionToAggregateExpression(_expression, args.DocumentSerializer, args.SerializationDomain, args.TranslationOptions, contextData); } } diff --git a/src/MongoDB.Driver/Authentication/MongoDBX509Authenticator.cs b/src/MongoDB.Driver/Authentication/MongoDBX509Authenticator.cs index dd4b9ca0688..3f6e0323dd3 100644 --- a/src/MongoDB.Driver/Authentication/MongoDBX509Authenticator.cs +++ b/src/MongoDB.Driver/Authentication/MongoDBX509Authenticator.cs @@ -17,6 +17,7 @@ using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Misc; @@ -30,7 +31,6 @@ public static string MechanismName { get { return "MONGODB-X509"; } } - private readonly string _username; private readonly ServerApi _serverApi; @@ -115,7 +115,8 @@ private CommandWireProtocol CreateAuthenticateProtocol() secondaryOk: true, resultSerializer: BsonDocumentSerializer.Instance, messageEncoderSettings: null, - serverApi: _serverApi); + serverApi: _serverApi, + serializationDomain: BsonSerializer.DefaultSerializationDomain); //QUESTION Is this correct? Using a default serialization domain? return protocol; } diff --git a/src/MongoDB.Driver/Authentication/Oidc/AzureOidcCallback.cs b/src/MongoDB.Driver/Authentication/Oidc/AzureOidcCallback.cs index 8b4c3100ee0..ab4c8a11571 100644 --- a/src/MongoDB.Driver/Authentication/Oidc/AzureOidcCallback.cs +++ b/src/MongoDB.Driver/Authentication/Oidc/AzureOidcCallback.cs @@ -46,7 +46,7 @@ protected override OidcAccessToken ProcessHttpResponse(Stream responseStream) using var responseReader = new StreamReader(responseStream); using var jsonReader = new JsonReader(responseReader); - var context = BsonDeserializationContext.CreateRoot(jsonReader); + var context = BsonDeserializationContext.CreateRoot(jsonReader, BsonSerializer.DefaultSerializationDomain); //QUESTION Is it ok to use the default domain here? var document = BsonDocumentSerializer.Instance.Deserialize(context); var accessToken = document.GetValue("access_token"); diff --git a/src/MongoDB.Driver/Authentication/SaslAuthenticator.cs b/src/MongoDB.Driver/Authentication/SaslAuthenticator.cs index d42558ddee6..035e164feba 100644 --- a/src/MongoDB.Driver/Authentication/SaslAuthenticator.cs +++ b/src/MongoDB.Driver/Authentication/SaslAuthenticator.cs @@ -19,6 +19,7 @@ using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Connections; using MongoDB.Driver.Core.Misc; @@ -250,7 +251,8 @@ private CommandWireProtocol CreateCommandProtocol(BsonDocument com secondaryOk: true, resultSerializer: BsonDocumentSerializer.Instance, messageEncoderSettings: null, - serverApi: _serverApi); + serverApi: _serverApi, + serializationDomain: BsonSerializer.DefaultSerializationDomain); //QUESTION Is it correct to use the default serialization domain here? private BsonDocument CreateStartCommand(byte[] bytesToSendToServer) { diff --git a/src/MongoDB.Driver/BsonSerializerExtensions.cs b/src/MongoDB.Driver/BsonSerializerExtensions.cs index f71b12a2d50..93e520a85a6 100644 --- a/src/MongoDB.Driver/BsonSerializerExtensions.cs +++ b/src/MongoDB.Driver/BsonSerializerExtensions.cs @@ -13,20 +13,21 @@ * limitations under the License. */ +using MongoDB.Bson; using MongoDB.Bson.Serialization; namespace MongoDB.Driver { internal static class BsonSerializerExtensions { - public static object SetDocumentIdIfMissing(this IBsonSerializer serializer, object container, TDocument document) + public static object SetDocumentIdIfMissing(this IBsonSerializer serializer, object container, TDocument document, IBsonSerializationDomain serializationDomain) { var idProvider = serializer as IBsonIdProvider; if (idProvider != null) { object id; IIdGenerator idGenerator; - if (idProvider.GetDocumentId(document, out id, out _, out idGenerator)) + if (idProvider.GetDocumentIdInternal(document, serializationDomain, out id, out _, out idGenerator)) { if (idGenerator != null && idGenerator.IsEmpty(id)) { diff --git a/src/MongoDB.Driver/ChangeStreamHelper.cs b/src/MongoDB.Driver/ChangeStreamHelper.cs index a92825e6d99..6c526742699 100644 --- a/src/MongoDB.Driver/ChangeStreamHelper.cs +++ b/src/MongoDB.Driver/ChangeStreamHelper.cs @@ -30,14 +30,16 @@ public static ChangeStreamOperation CreateChangeStreamOperation( renderedPipeline.Documents, renderedPipeline.OutputSerializer, - messageEncoderSettings) + messageEncoderSettings, + serializationDomain) { RetryRequested = retryRequested }; @@ -53,15 +55,17 @@ public static ChangeStreamOperation CreateChangeStreamOperation( database.DatabaseNamespace, renderedPipeline.Documents, renderedPipeline.OutputSerializer, - messageEncoderSettings) + messageEncoderSettings, + serializationDomain) { RetryRequested = retryRequested }; @@ -78,15 +82,17 @@ public static ChangeStreamOperation CreateChangeStreamOperation( collection.CollectionNamespace, renderedPipeline.Documents, renderedPipeline.OutputSerializer, - messageEncoderSettings) + messageEncoderSettings, + serializationDomain) { RetryRequested = retryRequested }; @@ -99,11 +105,11 @@ public static ChangeStreamOperation CreateChangeStreamOperation RenderPipeline( PipelineDefinition, TResult> pipeline, IBsonSerializer documentSerializer, + IBsonSerializationDomain serializationDomain, ExpressionTranslationOptions translationOptions) { var changeStreamDocumentSerializer = new ChangeStreamDocumentSerializer(documentSerializer); - var serializerRegistry = BsonSerializer.SerializerRegistry; - return pipeline.Render(new(changeStreamDocumentSerializer, serializerRegistry, translationOptions: translationOptions)); + return pipeline.Render(new(changeStreamDocumentSerializer, serializationDomain, translationOptions: translationOptions)); } private static void SetOperationOptions( diff --git a/src/MongoDB.Driver/ClusteredIndexOptions.cs b/src/MongoDB.Driver/ClusteredIndexOptions.cs index cb3b597ee19..64d886b6e1e 100644 --- a/src/MongoDB.Driver/ClusteredIndexOptions.cs +++ b/src/MongoDB.Driver/ClusteredIndexOptions.cs @@ -64,6 +64,7 @@ public bool Unique set => _unique = value; } + //DOMAIN-API This version will need to go away. internal BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry, ExpressionTranslationOptions translationOptions) { return new BsonDocument { @@ -72,5 +73,14 @@ internal BsonDocument Render(IBsonSerializer documentSerializer, IBso { "name", _name, _name != null } }; } + + internal BsonDocument Render(IBsonSerializer documentSerializer, IBsonSerializationDomain serializationDomain, ExpressionTranslationOptions translationOptions) + { + return new BsonDocument { + { "key", _key.Render(new(documentSerializer, serializationDomain, translationOptions: translationOptions)) }, + { "unique", _unique }, + { "name", _name, _name != null } + }; + } } } diff --git a/src/MongoDB.Driver/Core/Bindings/CoreSession.cs b/src/MongoDB.Driver/Core/Bindings/CoreSession.cs index 073492ea4da..8a2723dc15e 100644 --- a/src/MongoDB.Driver/Core/Bindings/CoreSession.cs +++ b/src/MongoDB.Driver/Core/Bindings/CoreSession.cs @@ -18,6 +18,7 @@ using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Operations; @@ -45,6 +46,7 @@ public sealed class CoreSession : ICoreSession private BsonTimestamp _snapshotTime; // constructors + //FP This constructor is only used by the tests. internal CoreSession( IClusterInternal cluster, ICoreServerSession serverSession, @@ -434,14 +436,16 @@ public void WasUsed() // private methods private IReadOperation CreateAbortTransactionOperation() { - return new AbortTransactionOperation(_currentTransaction.RecoveryToken, GetTransactionWriteConcern()); + //QUESTION Is it correct we only need a default domain here? + return new AbortTransactionOperation(_currentTransaction.RecoveryToken, GetTransactionWriteConcern(), BsonSerializer.DefaultSerializationDomain); } private IReadOperation CreateCommitTransactionOperation(bool isCommitRetry) { var writeConcern = GetCommitTransactionWriteConcern(isCommitRetry); var maxCommitTime = _currentTransaction.TransactionOptions.MaxCommitTime; - return new CommitTransactionOperation(_currentTransaction.RecoveryToken, writeConcern) { MaxCommitTime = maxCommitTime }; + //QUESTION Is it correct we only need a default domain here? + return new CommitTransactionOperation(_currentTransaction.RecoveryToken, writeConcern, BsonSerializer.DefaultSerializationDomain) { MaxCommitTime = maxCommitTime }; } private void EnsureAbortTransactionCanBeCalled(string methodName) diff --git a/src/MongoDB.Driver/Core/Bindings/IChannel.cs b/src/MongoDB.Driver/Core/Bindings/IChannel.cs index 3a1dca12020..b0dd2f7a8c5 100644 --- a/src/MongoDB.Driver/Core/Bindings/IChannel.cs +++ b/src/MongoDB.Driver/Core/Bindings/IChannel.cs @@ -31,6 +31,22 @@ internal interface IChannel : IDisposable IConnectionHandle Connection { get; } ConnectionDescription ConnectionDescription { get; } + TResult Command( + OperationContext operationContext, + ICoreSession session, + ReadPreference readPreference, + DatabaseNamespace databaseNamespace, + BsonDocument command, + IEnumerable commandPayloads, + IElementNameValidator commandValidator, + BsonDocument additionalOptions, + Action postWriteAction, + CommandResponseHandling responseHandling, + IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain); + + //EXIT TResult Command( OperationContext operationContext, ICoreSession session, @@ -45,6 +61,22 @@ TResult Command( IBsonSerializer resultSerializer, MessageEncoderSettings messageEncoderSettings); + Task CommandAsync( + OperationContext operationContext, + ICoreSession session, + ReadPreference readPreference, + DatabaseNamespace databaseNamespace, + BsonDocument command, + IEnumerable commandPayloads, + IElementNameValidator commandValidator, + BsonDocument additionalOptions, + Action postWriteAction, + CommandResponseHandling responseHandling, + IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain); + + //EXIT Task CommandAsync( OperationContext operationContext, ICoreSession session, diff --git a/src/MongoDB.Driver/Core/Connections/ConnectionInitializer.cs b/src/MongoDB.Driver/Core/Connections/ConnectionInitializer.cs index fdf95dbfe05..c50df1e1907 100644 --- a/src/MongoDB.Driver/Core/Connections/ConnectionInitializer.cs +++ b/src/MongoDB.Driver/Core/Connections/ConnectionInitializer.cs @@ -19,6 +19,7 @@ using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Authentication; using MongoDB.Driver.Core.Configuration; @@ -161,7 +162,8 @@ private CommandWireProtocol CreateGetLastErrorProtocol(ServerApi s secondaryOk: true, resultSerializer: BsonDocumentSerializer.Instance, messageEncoderSettings: null, - serverApi: serverApi); + serverApi: serverApi, + serializationDomain: BsonSerializer.DefaultSerializationDomain); //QUESTION Is it correct to use the default serialization domain here? return getLastErrorProtocol; } diff --git a/src/MongoDB.Driver/Core/Connections/HelloHelper.cs b/src/MongoDB.Driver/Core/Connections/HelloHelper.cs index 2ebebe12078..c206291741a 100644 --- a/src/MongoDB.Driver/Core/Connections/HelloHelper.cs +++ b/src/MongoDB.Driver/Core/Connections/HelloHelper.cs @@ -19,6 +19,7 @@ using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Authentication; using MongoDB.Driver.Core.Compression; @@ -80,7 +81,8 @@ internal static CommandWireProtocol CreateProtocol( commandResponseHandling: commandResponseHandling, resultSerializer: BsonDocumentSerializer.Instance, messageEncoderSettings: null, - serverApi); + serverApi, + serializationDomain: BsonSerializer.DefaultSerializationDomain); //QUESTION Is it correct to use the default serialization domain here? } internal static HelloResult GetResult( diff --git a/src/MongoDB.Driver/Core/Operations/AggregateOperation.cs b/src/MongoDB.Driver/Core/Operations/AggregateOperation.cs index 86eae237593..a1edb918736 100644 --- a/src/MongoDB.Driver/Core/Operations/AggregateOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/AggregateOperation.cs @@ -46,6 +46,7 @@ internal sealed class AggregateOperation : IReadOperation _pipeline; private ReadConcern _readConcern = ReadConcern.Default; private readonly IBsonSerializer _resultSerializer; + private readonly IBsonSerializationDomain _serializationDomain; private bool _retryRequested; private bool? _useCursor; @@ -57,12 +58,24 @@ internal sealed class AggregateOperation : IReadOperationThe pipeline. /// The result value serializer. /// The message encoder settings. - public AggregateOperation(DatabaseNamespace databaseNamespace, IEnumerable pipeline, IBsonSerializer resultSerializer, MessageEncoderSettings messageEncoderSettings) - : this(pipeline, resultSerializer, messageEncoderSettings) + /// The serialization domain. + public AggregateOperation(DatabaseNamespace databaseNamespace, + IEnumerable pipeline, IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) + : this(pipeline, resultSerializer, messageEncoderSettings, serializationDomain) { _databaseNamespace = Ensure.IsNotNull(databaseNamespace, nameof(databaseNamespace)); } + //EXIT + public AggregateOperation(DatabaseNamespace databaseNamespace, + IEnumerable pipeline, IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings) + : this(databaseNamespace, pipeline, resultSerializer, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain) + { + } + /// /// Initializes a new instance of the class. /// @@ -70,17 +83,35 @@ public AggregateOperation(DatabaseNamespace databaseNamespace, IEnumerableThe pipeline. /// The result value serializer. /// The message encoder settings. - public AggregateOperation(CollectionNamespace collectionNamespace, IEnumerable pipeline, IBsonSerializer resultSerializer, MessageEncoderSettings messageEncoderSettings) - : this(pipeline, resultSerializer, messageEncoderSettings) + /// The serialization domain. + public AggregateOperation(CollectionNamespace collectionNamespace, + IEnumerable pipeline, + IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) + : this(pipeline, resultSerializer, messageEncoderSettings, serializationDomain) { _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); } - private AggregateOperation(IEnumerable pipeline, IBsonSerializer resultSerializer, MessageEncoderSettings messageEncoderSettings) + //EXIT + public AggregateOperation(CollectionNamespace collectionNamespace, + IEnumerable pipeline, + IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings) + : this(collectionNamespace, pipeline, resultSerializer, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain) + { + } + + private AggregateOperation(IEnumerable pipeline, + IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) { _pipeline = Ensure.IsNotNull(pipeline, nameof(pipeline)).ToList(); _resultSerializer = Ensure.IsNotNull(resultSerializer, nameof(resultSerializer)); _messageEncoderSettings = Ensure.IsNotNull(messageEncoderSettings, nameof(messageEncoderSettings)); + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); } // properties @@ -359,7 +390,7 @@ private ReadCommandOperation CreateOperation(RetryableReadConte var databaseNamespace = _collectionNamespace == null ? _databaseNamespace : _collectionNamespace.DatabaseNamespace; var command = CreateCommand(context.Channel.ConnectionDescription, context.Binding.Session); var serializer = new AggregateResultDeserializer(_resultSerializer); - return new ReadCommandOperation(databaseNamespace, command, serializer, MessageEncoderSettings) + return new ReadCommandOperation(databaseNamespace, command, serializer, MessageEncoderSettings, _serializationDomain) { RetryRequested = _retryRequested // might be overridden by retryable read context }; @@ -393,6 +424,7 @@ private AsyncCursor CreateCursorFromCursorResult(IChannelSourceHandle c null, // limit _resultSerializer, MessageEncoderSettings, + _serializationDomain, _maxAwaitTime); } @@ -409,6 +441,7 @@ private AsyncCursor CreateCursorFromInlineResult(AggregateResult result null, // limit _resultSerializer, MessageEncoderSettings, + _serializationDomain, _maxAwaitTime); } diff --git a/src/MongoDB.Driver/Core/Operations/AggregateToCollectionOperation.cs b/src/MongoDB.Driver/Core/Operations/AggregateToCollectionOperation.cs index 79c684d9752..e45f9f13025 100644 --- a/src/MongoDB.Driver/Core/Operations/AggregateToCollectionOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/AggregateToCollectionOperation.cs @@ -18,6 +18,7 @@ using System.Linq; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Connections; @@ -44,23 +45,37 @@ internal sealed class AggregateToCollectionOperation : IWriteOperation pipeline, MessageEncoderSettings messageEncoderSettings) + public AggregateToCollectionOperation(DatabaseNamespace databaseNamespace, IEnumerable pipeline, MessageEncoderSettings messageEncoderSettings, IBsonSerializationDomain serializationDomain) { _databaseNamespace = Ensure.IsNotNull(databaseNamespace, nameof(databaseNamespace)); _pipeline = Ensure.IsNotNull(pipeline, nameof(pipeline)).ToList(); _messageEncoderSettings = Ensure.IsNotNull(messageEncoderSettings, nameof(messageEncoderSettings)); + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); EnsureIsOutputToCollectionPipeline(); _pipeline = SimplifyOutStageIfOutputDatabaseIsSameAsInputDatabase(_pipeline); } - public AggregateToCollectionOperation(CollectionNamespace collectionNamespace, IEnumerable pipeline, MessageEncoderSettings messageEncoderSettings) - : this(Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)).DatabaseNamespace, pipeline, messageEncoderSettings) + //EXIT + public AggregateToCollectionOperation(DatabaseNamespace databaseNamespace, IEnumerable pipeline, MessageEncoderSettings messageEncoderSettings) + : this(databaseNamespace, pipeline, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain) + { + } + + public AggregateToCollectionOperation(CollectionNamespace collectionNamespace, IEnumerable pipeline, MessageEncoderSettings messageEncoderSettings, IBsonSerializationDomain serializationDomain) + : this(Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)).DatabaseNamespace, pipeline, messageEncoderSettings, serializationDomain) { _collectionNamespace = collectionNamespace; } + //EXIT + public AggregateToCollectionOperation(CollectionNamespace collectionNamespace, IEnumerable pipeline, MessageEncoderSettings messageEncoderSettings) + : this(collectionNamespace, pipeline, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain) + { + } + public bool? AllowDiskUse { get { return _allowDiskUse; } @@ -205,7 +220,7 @@ public BsonDocument CreateCommand(ICoreSessionHandle session, ConnectionDescript private WriteCommandOperation CreateOperation(ICoreSessionHandle session, ConnectionDescription connectionDescription, ReadPreference effectiveReadPreference) { var command = CreateCommand(session, connectionDescription); - var operation = new WriteCommandOperation(_databaseNamespace, command, BsonDocumentSerializer.Instance, MessageEncoderSettings); + var operation = new WriteCommandOperation(_databaseNamespace, command, BsonDocumentSerializer.Instance, MessageEncoderSettings, _serializationDomain); if (effectiveReadPreference != null) { operation.ReadPreference = effectiveReadPreference; diff --git a/src/MongoDB.Driver/Core/Operations/AsyncCursor.cs b/src/MongoDB.Driver/Core/Operations/AsyncCursor.cs index dd5c7e0ba9f..6b71a6ccb48 100644 --- a/src/MongoDB.Driver/Core/Operations/AsyncCursor.cs +++ b/src/MongoDB.Driver/Core/Operations/AsyncCursor.cs @@ -53,6 +53,7 @@ internal class AsyncCursor : IAsyncCursor, ICursorBatchInf private readonly int? _limit; private readonly TimeSpan? _maxTime; private readonly MessageEncoderSettings _messageEncoderSettings; + private readonly IBsonSerializationDomain _serializationDomain; private readonly long? _operationId; private BsonDocument _postBatchResumeToken; private readonly IBsonSerializer _serializer; @@ -68,6 +69,7 @@ public AsyncCursor( int? limit, IBsonSerializer serializer, MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain, TimeSpan? maxTime = null) : this( channelSource, @@ -80,6 +82,34 @@ public AsyncCursor( limit, serializer, messageEncoderSettings, + serializationDomain, + maxTime) + { + } + + //EXIT + public AsyncCursor( + IChannelSource channelSource, + CollectionNamespace collectionNamespace, + BsonValue comment, + IReadOnlyList firstBatch, + long cursorId, + int? batchSize, + int? limit, + IBsonSerializer serializer, + MessageEncoderSettings messageEncoderSettings, + TimeSpan? maxTime = null) + : this( + channelSource, + collectionNamespace, + comment, + firstBatch, + cursorId, + batchSize, // postBatchResumeToken + limit, + serializer, + messageEncoderSettings, + BsonSerializer.DefaultSerializationDomain, maxTime) { } @@ -95,6 +125,7 @@ public AsyncCursor( int? limit, IBsonSerializer serializer, MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain, TimeSpan? maxTime) { _operationId = EventContext.OperationId; @@ -108,6 +139,7 @@ public AsyncCursor( _limit = Ensure.IsNullOrGreaterThanOrEqualToZero(limit, nameof(limit)); _serializer = Ensure.IsNotNull(serializer, nameof(serializer)); _messageEncoderSettings = messageEncoderSettings; + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); _maxTime = maxTime; if (_limit > 0 && _firstBatch.Count > _limit) @@ -120,6 +152,35 @@ public AsyncCursor( DisposeChannelSourceIfNoLongerNeeded(); } + //EXIT + public AsyncCursor( + IChannelSource channelSource, + CollectionNamespace collectionNamespace, + BsonValue comment, + IReadOnlyList firstBatch, + long cursorId, + BsonDocument postBatchResumeToken, + int? batchSize, + int? limit, + IBsonSerializer serializer, + MessageEncoderSettings messageEncoderSettings, + TimeSpan? maxTime) + : this( + channelSource, + collectionNamespace, + comment, + firstBatch, + cursorId, + postBatchResumeToken, + batchSize, + limit, + serializer, + messageEncoderSettings, + BsonSerializer.DefaultSerializationDomain, + maxTime) + { + } + public IEnumerable Current { get @@ -183,7 +244,7 @@ private CursorBatch CreateCursorBatch(BsonDocument result) using (batch) { - var documents = CursorBatchDeserializationHelper.DeserializeBatch(batch, _serializer, _messageEncoderSettings); + var documents = CursorBatchDeserializationHelper.DeserializeBatch(batch, _serializer, _messageEncoderSettings, _serializationDomain); return new CursorBatch(cursorId, postBatchResumeToken, documents); } } @@ -233,7 +294,8 @@ private CursorBatch ExecuteGetMoreCommand(IChannelHandle channel, Can null, // postWriteAction CommandResponseHandling.Return, __getMoreCommandResultSerializer, - _messageEncoderSettings); + _messageEncoderSettings, + _serializationDomain); } catch (MongoCommandException ex) when (IsMongoCursorNotFoundException(ex)) { @@ -263,7 +325,8 @@ private async Task> ExecuteGetMoreCommandAsync(IChannelHa null, // postWriteAction CommandResponseHandling.Return, __getMoreCommandResultSerializer, - _messageEncoderSettings).ConfigureAwait(false); + _messageEncoderSettings, + _serializationDomain).ConfigureAwait(false); } catch (MongoCommandException ex) when (IsMongoCursorNotFoundException(ex)) { @@ -290,7 +353,8 @@ private void ExecuteKillCursorsCommand(IChannelHandle channel, CancellationToken null, // postWriteAction CommandResponseHandling.Return, BsonDocumentSerializer.Instance, - _messageEncoderSettings); + _messageEncoderSettings, + _serializationDomain); ThrowIfKillCursorsCommandFailed(result, channel.ConnectionDescription.ConnectionId); } @@ -312,7 +376,8 @@ private async Task ExecuteKillCursorsCommandAsync(IChannelHandle channel, Cancel null, // postWriteAction CommandResponseHandling.Return, BsonDocumentSerializer.Instance, - _messageEncoderSettings) + _messageEncoderSettings, + _serializationDomain) .ConfigureAwait(false); ThrowIfKillCursorsCommandFailed(result, channel.ConnectionDescription.ConnectionId); diff --git a/src/MongoDB.Driver/Core/Operations/ChangeStreamCursor.cs b/src/MongoDB.Driver/Core/Operations/ChangeStreamCursor.cs index afd01623847..b3ce1d25849 100644 --- a/src/MongoDB.Driver/Core/Operations/ChangeStreamCursor.cs +++ b/src/MongoDB.Driver/Core/Operations/ChangeStreamCursor.cs @@ -46,6 +46,7 @@ internal sealed class ChangeStreamCursor : IChangeStreamCursor @@ -65,6 +66,7 @@ internal sealed class ChangeStreamCursor : IChangeStreamCursorThe resume after value. /// The start at operation time value. /// The maximum wire version. + /// The serialization domain. public ChangeStreamCursor( IAsyncCursor cursor, IBsonSerializer documentSerializer, @@ -75,7 +77,8 @@ public ChangeStreamCursor( BsonDocument initialStartAfter, BsonDocument initialResumeAfter, BsonTimestamp initialStartAtOperationTime, - int maxWireVersion) + int maxWireVersion, + IBsonSerializationDomain serializationDomain) { _cursor = Ensure.IsNotNull(cursor, nameof(cursor)); _documentSerializer = Ensure.IsNotNull(documentSerializer, nameof(documentSerializer)); @@ -88,6 +91,34 @@ public ChangeStreamCursor( _initialResumeAfter = initialResumeAfter; _initialStartAtOperationTime = initialStartAtOperationTime; _maxWireVersion = Ensure.IsGreaterThanOrEqualToZero(maxWireVersion, nameof(maxWireVersion)); + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); + } + + //EXIT + public ChangeStreamCursor( + IAsyncCursor cursor, + IBsonSerializer documentSerializer, + IReadBinding binding, + IChangeStreamOperation changeStreamOperation, + BsonDocument aggregatePostBatchResumeToken, + BsonTimestamp initialOperationTime, + BsonDocument initialStartAfter, + BsonDocument initialResumeAfter, + BsonTimestamp initialStartAtOperationTime, + int maxWireVersion) + : this( + cursor, + documentSerializer, + binding, + changeStreamOperation, + aggregatePostBatchResumeToken, + initialOperationTime, + initialStartAfter, + initialResumeAfter, + initialStartAtOperationTime, + maxWireVersion, + BsonSerializer.DefaultSerializationDomain) + { } // public methods @@ -165,7 +196,7 @@ private TDocument DeserializeDocument(RawBsonDocument rawDocument) using (var stream = new ByteBufferStream(rawDocument.Slice, ownsBuffer: false)) using (var reader = new BsonBinaryReader(stream)) { - var context = BsonDeserializationContext.CreateRoot(reader); + var context = BsonDeserializationContext.CreateRoot(reader, _serializationDomain); return _documentSerializer.Deserialize(context); } } diff --git a/src/MongoDB.Driver/Core/Operations/ChangeStreamOperation.cs b/src/MongoDB.Driver/Core/Operations/ChangeStreamOperation.cs index ed08f1c85e5..0fc86ffcbed 100644 --- a/src/MongoDB.Driver/Core/Operations/ChangeStreamOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/ChangeStreamOperation.cs @@ -53,6 +53,7 @@ internal sealed class ChangeStreamOperation : IChangeStreamOperation _resultSerializer; private BsonDocument _resumeAfter; private bool _retryRequested; + private readonly IBsonSerializationDomain _serializationDomain; private bool? _showExpandedEvents; private BsonDocument _startAfter; private BsonTimestamp _startAtOperationTime; @@ -60,33 +61,60 @@ internal sealed class ChangeStreamOperation : IChangeStreamOperation pipeline, IBsonSerializer resultSerializer, - MessageEncoderSettings messageEncoderSettings) + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) { _pipeline = Ensure.IsNotNull(pipeline, nameof(pipeline)).ToList(); _resultSerializer = Ensure.IsNotNull(resultSerializer, nameof(resultSerializer)); _messageEncoderSettings = Ensure.IsNotNull(messageEncoderSettings, nameof(messageEncoderSettings)); + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); } + //EXIT public ChangeStreamOperation( DatabaseNamespace databaseNamespace, IEnumerable pipeline, IBsonSerializer resultSerializer, - MessageEncoderSettings messageEncoderSettings) - : this(pipeline, resultSerializer, messageEncoderSettings) + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) + : this(pipeline, resultSerializer, messageEncoderSettings, serializationDomain) { _databaseNamespace = Ensure.IsNotNull(databaseNamespace, nameof(databaseNamespace)); } + //EXIT public ChangeStreamOperation( - CollectionNamespace collectionNamespace, + DatabaseNamespace databaseNamespace, IEnumerable pipeline, IBsonSerializer resultSerializer, MessageEncoderSettings messageEncoderSettings) - : this(pipeline, resultSerializer, messageEncoderSettings) + : this(databaseNamespace, pipeline, resultSerializer, messageEncoderSettings, + BsonSerializer.DefaultSerializationDomain) + { + } + + public ChangeStreamOperation( + CollectionNamespace collectionNamespace, + IEnumerable pipeline, + IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) + : this(pipeline, resultSerializer, messageEncoderSettings, serializationDomain) { _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); } + //EXIT + public ChangeStreamOperation( + CollectionNamespace collectionNamespace, + IEnumerable pipeline, + IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings) + : this(collectionNamespace, pipeline, resultSerializer, messageEncoderSettings, + BsonSerializer.DefaultSerializationDomain) + { + } + // public properties /// /// Gets or sets the size of the batch. @@ -279,7 +307,8 @@ public IChangeStreamCursor Execute(OperationContext operationContext, I _startAfter, _resumeAfter, _startAtOperationTime, - context.Channel.ConnectionDescription.MaxWireVersion); + context.Channel.ConnectionDescription.MaxWireVersion, + _serializationDomain); } } @@ -314,7 +343,8 @@ public async Task> ExecuteAsync(OperationContext op _startAfter, _resumeAfter, _startAtOperationTime, - context.Channel.ConnectionDescription.MaxWireVersion); + context.Channel.ConnectionDescription.MaxWireVersion, + _serializationDomain); } } @@ -345,7 +375,7 @@ private AggregateOperation CreateAggregateOperation() AggregateOperation operation; if (_collectionNamespace != null) { - operation = new AggregateOperation(_collectionNamespace, combinedPipeline, RawBsonDocumentSerializer.Instance, _messageEncoderSettings) + operation = new AggregateOperation(_collectionNamespace, combinedPipeline, RawBsonDocumentSerializer.Instance, _messageEncoderSettings, _serializationDomain) { RetryRequested = _retryRequested // might be overridden by retryable read context }; @@ -353,7 +383,7 @@ private AggregateOperation CreateAggregateOperation() else { var databaseNamespace = _databaseNamespace ?? DatabaseNamespace.Admin; - operation = new AggregateOperation(databaseNamespace, combinedPipeline, RawBsonDocumentSerializer.Instance, _messageEncoderSettings) + operation = new AggregateOperation(databaseNamespace, combinedPipeline, RawBsonDocumentSerializer.Instance, _messageEncoderSettings, _serializationDomain) { RetryRequested = _retryRequested // might be overridden by retryable read context }; diff --git a/src/MongoDB.Driver/Core/Operations/ClientBulkWriteOperation.cs b/src/MongoDB.Driver/Core/Operations/ClientBulkWriteOperation.cs index 2416fd0aff3..cb5a1300188 100644 --- a/src/MongoDB.Driver/Core/Operations/ClientBulkWriteOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/ClientBulkWriteOperation.cs @@ -18,6 +18,7 @@ using System.Linq; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Connections; @@ -41,8 +42,9 @@ public ClientBulkWriteOperation( IReadOnlyList writeModels, ClientBulkWriteOptions options, MessageEncoderSettings messageEncoderSettings, - RenderArgs renderArgs) - : base(DatabaseNamespace.Admin, messageEncoderSettings) + RenderArgs renderArgs, + IBsonSerializationDomain serializationDomain) + : base(DatabaseNamespace.Admin, messageEncoderSettings, serializationDomain) { Ensure.IsNotNullOrEmpty(writeModels, nameof(writeModels)); _writeModels = new BatchableSource(writeModels, true); @@ -279,7 +281,8 @@ private IAsyncCursor GetIndividualResultsCursor(RetryableWriteCont 0, 0, BsonDocumentSerializer.Instance, - MessageEncoderSettings); + MessageEncoderSettings, + SerializationDomain); } private void PopulateBulkWriteResponse(BsonDocument bulkWriteResponse, BulkWriteRawResult bulkWriteResult) diff --git a/src/MongoDB.Driver/Core/Operations/CommandOperationBase.cs b/src/MongoDB.Driver/Core/Operations/CommandOperationBase.cs index e5f94ebe200..2b3f10edde4 100644 --- a/src/MongoDB.Driver/Core/Operations/CommandOperationBase.cs +++ b/src/MongoDB.Driver/Core/Operations/CommandOperationBase.cs @@ -33,17 +33,35 @@ internal abstract class CommandOperationBase private DatabaseNamespace _databaseNamespace; private MessageEncoderSettings _messageEncoderSettings; private IBsonSerializer _resultSerializer; + private IBsonSerializationDomain _serializationDomain; protected CommandOperationBase( DatabaseNamespace databaseNamespace, BsonDocument command, IBsonSerializer resultSerializer, - MessageEncoderSettings messageEncoderSettings) + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) { _databaseNamespace = Ensure.IsNotNull(databaseNamespace, nameof(databaseNamespace)); _command = Ensure.IsNotNull(command, nameof(command)); _resultSerializer = Ensure.IsNotNull(resultSerializer, nameof(resultSerializer)); _messageEncoderSettings = messageEncoderSettings; + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); + } + + //EXIT + protected CommandOperationBase( + DatabaseNamespace databaseNamespace, + BsonDocument command, + IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings) + : this( + databaseNamespace, + command, + resultSerializer, + messageEncoderSettings, + BsonSerializer.DefaultSerializationDomain) + { } public BsonDocument AdditionalOptions @@ -100,7 +118,8 @@ protected TCommandResult ExecuteProtocol(OperationContext operationContext, ICha null, // postWriteAction, CommandResponseHandling.Return, _resultSerializer, - _messageEncoderSettings); + _messageEncoderSettings, + _serializationDomain); } protected TCommandResult ExecuteProtocol( @@ -131,7 +150,8 @@ protected Task ExecuteProtocolAsync(OperationContext operationCo null, // postWriteAction, CommandResponseHandling.Return, _resultSerializer, - _messageEncoderSettings); + _messageEncoderSettings, + _serializationDomain); } protected async Task ExecuteProtocolAsync( diff --git a/src/MongoDB.Driver/Core/Operations/CountDocumentsOperation.cs b/src/MongoDB.Driver/Core/Operations/CountDocumentsOperation.cs index 6bcc00d33db..fd3fc747e49 100644 --- a/src/MongoDB.Driver/Core/Operations/CountDocumentsOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/CountDocumentsOperation.cs @@ -17,6 +17,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Events; @@ -38,11 +39,19 @@ internal sealed class CountDocumentsOperation : IReadOperation private ReadConcern _readConcern = ReadConcern.Default; private bool _retryRequested; private long? _skip; + private IBsonSerializationDomain _serializationDomain; - public CountDocumentsOperation(CollectionNamespace collectionNamespace, MessageEncoderSettings messageEncoderSettings) + public CountDocumentsOperation(CollectionNamespace collectionNamespace, MessageEncoderSettings messageEncoderSettings, IBsonSerializationDomain serializationDomain) { _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); _messageEncoderSettings = Ensure.IsNotNull(messageEncoderSettings, nameof(messageEncoderSettings)); + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); + } + + //EXIT + public CountDocumentsOperation(CollectionNamespace collectionNamespace, MessageEncoderSettings messageEncoderSettings) + : this(collectionNamespace, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain) + { } public Collation Collation @@ -140,7 +149,7 @@ public async Task ExecuteAsync(OperationContext operationContext, IReadBin private AggregateOperation CreateOperation() { var pipeline = CreatePipeline(); - var operation = new AggregateOperation(_collectionNamespace, pipeline, BsonDocumentSerializer.Instance, _messageEncoderSettings) + var operation = new AggregateOperation(_collectionNamespace, pipeline, BsonDocumentSerializer.Instance, _messageEncoderSettings, _serializationDomain) { Collation = _collation, Comment = _comment, diff --git a/src/MongoDB.Driver/Core/Operations/CountOperation.cs b/src/MongoDB.Driver/Core/Operations/CountOperation.cs index cd83e1d374d..1aa84eb8354 100644 --- a/src/MongoDB.Driver/Core/Operations/CountOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/CountOperation.cs @@ -16,6 +16,7 @@ using System; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Connections; @@ -37,12 +38,20 @@ internal sealed class CountOperation : IReadOperation, IExecutableInRetrya private readonly MessageEncoderSettings _messageEncoderSettings; private ReadConcern _readConcern = ReadConcern.Default; private bool _retryRequested; + private IBsonSerializationDomain _serializationDomain; private long? _skip; - public CountOperation(CollectionNamespace collectionNamespace, MessageEncoderSettings messageEncoderSettings) + public CountOperation(CollectionNamespace collectionNamespace, MessageEncoderSettings messageEncoderSettings, IBsonSerializationDomain serializationDomain) { _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); _messageEncoderSettings = Ensure.IsNotNull(messageEncoderSettings, nameof(messageEncoderSettings)); + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); + } + + //EXIT + public CountOperation(CollectionNamespace collectionNamespace, MessageEncoderSettings messageEncoderSettings) + : this(collectionNamespace, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain) + { } public Collation Collation @@ -167,7 +176,7 @@ public async Task ExecuteAsync(OperationContext operationContext, Retryabl private ReadCommandOperation CreateOperation(RetryableReadContext context) { var command = CreateCommand(context.Channel.ConnectionDescription, context.Binding.Session); - return new ReadCommandOperation(_collectionNamespace.DatabaseNamespace, command, BsonDocumentSerializer.Instance, _messageEncoderSettings) + return new ReadCommandOperation(_collectionNamespace.DatabaseNamespace, command, BsonDocumentSerializer.Instance, _messageEncoderSettings, _serializationDomain) { RetryRequested = _retryRequested // might be overridden by retryable read context }; diff --git a/src/MongoDB.Driver/Core/Operations/CreateCollectionOperation.cs b/src/MongoDB.Driver/Core/Operations/CreateCollectionOperation.cs index c6e72696e37..9f732742d92 100644 --- a/src/MongoDB.Driver/Core/Operations/CreateCollectionOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/CreateCollectionOperation.cs @@ -16,6 +16,7 @@ using System; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Events; @@ -34,11 +35,13 @@ public static IWriteOperation CreateEncryptedCreateCollectionOpera CollectionNamespace collectionNamespace, BsonDocument encryptedFields, MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain, Action createCollectionOperationConfigurator) { var mainOperation = new CreateCollectionOperation( collectionNamespace, messageEncoderSettings, + serializationDomain, encryptedFields != null ? Feature.Csfle2QEv2 : null) { EncryptedFields = encryptedFields @@ -52,7 +55,7 @@ public static IWriteOperation CreateEncryptedCreateCollectionOpera (CreateInnerCollectionOperation(EncryptedCollectionHelper.GetAdditionalCollectionName(encryptedFields, collectionNamespace, HelperCollectionForEncryption.Esc)), IsMainOperation: false), (CreateInnerCollectionOperation(EncryptedCollectionHelper.GetAdditionalCollectionName(encryptedFields, collectionNamespace, HelperCollectionForEncryption.Ecos)), IsMainOperation: false), (mainOperation, IsMainOperation: true), - (new CreateIndexesOperation(collectionNamespace, new[] { new CreateIndexRequest(EncryptedCollectionHelper.AdditionalCreateIndexDocument) }, messageEncoderSettings), IsMainOperation: false)); + (new CreateIndexesOperation(collectionNamespace, new[] { new CreateIndexRequest(EncryptedCollectionHelper.AdditionalCreateIndexDocument) }, messageEncoderSettings, serializationDomain), IsMainOperation: false)); } else { @@ -60,7 +63,7 @@ public static IWriteOperation CreateEncryptedCreateCollectionOpera } CreateCollectionOperation CreateInnerCollectionOperation(string collectionName) - => new(new CollectionNamespace(collectionNamespace.DatabaseNamespace.DatabaseName, collectionName), messageEncoderSettings, Feature.Csfle2QEv2) + => new(new CollectionNamespace(collectionNamespace.DatabaseNamespace.DatabaseName, collectionName), messageEncoderSettings, serializationDomain, Feature.Csfle2QEv2) { ClusteredIndex = new BsonDocument { { "key", new BsonDocument("_id", 1) }, { "unique", true } } }; @@ -79,6 +82,7 @@ CreateCollectionOperation CreateInnerCollectionOperation(string collectionName) private long? _maxDocuments; private long? _maxSize; private readonly MessageEncoderSettings _messageEncoderSettings; + private readonly IBsonSerializationDomain _serializationDomain; private BsonDocument _storageEngine; private TimeSeriesOptions _timeSeriesOptions; private DocumentValidationAction? _validationAction; @@ -88,21 +92,32 @@ CreateCollectionOperation CreateInnerCollectionOperation(string collectionName) private readonly Feature _supportedFeature; + public CreateCollectionOperation( + CollectionNamespace collectionNamespace, + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) + : this(collectionNamespace, messageEncoderSettings, serializationDomain, supportedFeature: null) + { + } + + //EXIT public CreateCollectionOperation( CollectionNamespace collectionNamespace, MessageEncoderSettings messageEncoderSettings) - : this(collectionNamespace, messageEncoderSettings, supportedFeature: null) + : this(collectionNamespace, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain) { } private CreateCollectionOperation( CollectionNamespace collectionNamespace, MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain, Feature supportedFeature) { _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); _messageEncoderSettings = messageEncoderSettings; _supportedFeature = supportedFeature; + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); } public bool? Capped @@ -275,7 +290,7 @@ public async Task ExecuteAsync(OperationContext operationContext, private WriteCommandOperation CreateOperation(ICoreSessionHandle session) { var command = CreateCommand(session); - return new WriteCommandOperation(_collectionNamespace.DatabaseNamespace, command, BsonDocumentSerializer.Instance, _messageEncoderSettings); + return new WriteCommandOperation(_collectionNamespace.DatabaseNamespace, command, BsonDocumentSerializer.Instance, _messageEncoderSettings, _serializationDomain); } private void EnsureServerIsValid(int maxWireVersion) diff --git a/src/MongoDB.Driver/Core/Operations/CreateIndexesOperation.cs b/src/MongoDB.Driver/Core/Operations/CreateIndexesOperation.cs index f4e071950ad..1334cff20b5 100644 --- a/src/MongoDB.Driver/Core/Operations/CreateIndexesOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/CreateIndexesOperation.cs @@ -18,6 +18,7 @@ using System.Linq; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Connections; @@ -35,16 +36,28 @@ internal sealed class CreateIndexesOperation : IWriteOperation private TimeSpan? _maxTime; private readonly MessageEncoderSettings _messageEncoderSettings; private readonly IEnumerable _requests; + private readonly IBsonSerializationDomain _serializationDomain; private WriteConcern _writeConcern = WriteConcern.Acknowledged; public CreateIndexesOperation( CollectionNamespace collectionNamespace, IEnumerable requests, - MessageEncoderSettings messageEncoderSettings) + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) { _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); _requests = Ensure.IsNotNull(requests, nameof(requests)).ToList(); _messageEncoderSettings = Ensure.IsNotNull(messageEncoderSettings, nameof(messageEncoderSettings)); + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); + } + + //EXIT + public CreateIndexesOperation( + CollectionNamespace collectionNamespace, + IEnumerable requests, + MessageEncoderSettings messageEncoderSettings) + :this(collectionNamespace, requests, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain) + { } public CollectionNamespace CollectionNamespace @@ -137,7 +150,7 @@ private WriteCommandOperation CreateOperation(ICoreSessionHandle s var databaseNamespace = _collectionNamespace.DatabaseNamespace; var command = CreateCommand(session, connectionDescription); var resultSerializer = BsonDocumentSerializer.Instance; - return new WriteCommandOperation(databaseNamespace, command, resultSerializer, _messageEncoderSettings); + return new WriteCommandOperation(databaseNamespace, command, resultSerializer, _messageEncoderSettings, _serializationDomain); } } } diff --git a/src/MongoDB.Driver/Core/Operations/CreateSearchIndexesOperation.cs b/src/MongoDB.Driver/Core/Operations/CreateSearchIndexesOperation.cs index edc8ae04f77..43210821099 100644 --- a/src/MongoDB.Driver/Core/Operations/CreateSearchIndexesOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/CreateSearchIndexesOperation.cs @@ -17,6 +17,7 @@ using System.Linq; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Events; @@ -34,7 +35,9 @@ internal sealed class CreateSearchIndexesOperation : IWriteOperation _requests; + private readonly IBsonSerializationDomain _serializationDomain; + //FP We should remove all the docs in those internal classes, at least for constructors. // constructors /// /// Initializes a new instance of the class. @@ -42,14 +45,17 @@ internal sealed class CreateSearchIndexesOperation : IWriteOperationThe collection namespace. /// The requests. /// The message encoder settings. + /// public CreateSearchIndexesOperation( CollectionNamespace collectionNamespace, IEnumerable requests, - MessageEncoderSettings messageEncoderSettings) + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) { _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); _requests = Ensure.IsNotNull(requests, nameof(requests)).ToArray(); _messageEncoderSettings = Ensure.IsNotNull(messageEncoderSettings, nameof(messageEncoderSettings)); + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); } // public methods @@ -88,7 +94,7 @@ private WriteCommandOperation CreateOperation() { "indexes", new BsonArray(_requests.Select(request => request.CreateIndexDocument())) } }; - return new(_collectionNamespace.DatabaseNamespace, command, BsonDocumentSerializer.Instance, _messageEncoderSettings); + return new(_collectionNamespace.DatabaseNamespace, command, BsonDocumentSerializer.Instance, _messageEncoderSettings, _serializationDomain); } } } diff --git a/src/MongoDB.Driver/Core/Operations/CreateViewOperation.cs b/src/MongoDB.Driver/Core/Operations/CreateViewOperation.cs index 64a5d954bc2..672f557422e 100644 --- a/src/MongoDB.Driver/Core/Operations/CreateViewOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/CreateViewOperation.cs @@ -17,6 +17,7 @@ using System.Linq; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Connections; @@ -31,6 +32,7 @@ internal sealed class CreateViewOperation : IWriteOperation private readonly DatabaseNamespace _databaseNamespace; private readonly MessageEncoderSettings _messageEncoderSettings; private readonly IReadOnlyList _pipeline; + private readonly IBsonSerializationDomain _serializationDomain; private readonly string _viewName; private readonly string _viewOn; private WriteConcern _writeConcern; @@ -40,13 +42,26 @@ public CreateViewOperation( string viewName, string viewOn, IEnumerable pipeline, - MessageEncoderSettings messageEncoderSettings) + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) { _databaseNamespace = Ensure.IsNotNull(databaseNamespace, nameof(databaseNamespace)); _viewName = Ensure.IsNotNull(viewName, nameof(viewName)); _viewOn = Ensure.IsNotNull(viewOn, nameof(viewOn)); _pipeline = Ensure.IsNotNull(pipeline, nameof(pipeline)).ToList(); _messageEncoderSettings = Ensure.IsNotNull(messageEncoderSettings, nameof(messageEncoderSettings)); + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); + } + + //EXIT + public CreateViewOperation( + DatabaseNamespace databaseNamespace, + string viewName, + string viewOn, + IEnumerable pipeline, + MessageEncoderSettings messageEncoderSettings) + : this(databaseNamespace, viewName, viewOn, pipeline, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain) + { } public Collation Collation @@ -128,7 +143,7 @@ public BsonDocument CreateCommand(ICoreSessionHandle session, ConnectionDescript private WriteCommandOperation CreateOperation(ICoreSessionHandle session, ConnectionDescription connectionDescription) { var command = CreateCommand(session, connectionDescription); - return new WriteCommandOperation(_databaseNamespace, command, BsonDocumentSerializer.Instance, _messageEncoderSettings); + return new WriteCommandOperation(_databaseNamespace, command, BsonDocumentSerializer.Instance, _messageEncoderSettings, _serializationDomain); } } } diff --git a/src/MongoDB.Driver/Core/Operations/CursorBatchDeserializationHelper.cs b/src/MongoDB.Driver/Core/Operations/CursorBatchDeserializationHelper.cs index 27ff4252dc0..6b5a850e72f 100644 --- a/src/MongoDB.Driver/Core/Operations/CursorBatchDeserializationHelper.cs +++ b/src/MongoDB.Driver/Core/Operations/CursorBatchDeserializationHelper.cs @@ -34,9 +34,10 @@ internal static class CursorBatchDeserializationHelper /// The batch. /// The document serializer. /// The message encoder settings. + /// The serialization domain. /// The documents. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")] - public static List DeserializeBatch(RawBsonArray batch, IBsonSerializer documentSerializer, MessageEncoderSettings messageEncoderSettings) + public static List DeserializeBatch(RawBsonArray batch, IBsonSerializer documentSerializer, MessageEncoderSettings messageEncoderSettings, IBsonSerializationDomain serializationDomain) { var documents = new List(); @@ -49,7 +50,7 @@ public static List DeserializeBatch(RawBsonArray batch, IB using (var stream = new ByteBufferStream(batch.Slice, ownsBuffer: false)) using (var reader = new BsonBinaryReader(stream, readerSettings)) { - var context = BsonDeserializationContext.CreateRoot(reader); + var context = BsonDeserializationContext.CreateRoot(reader, serializationDomain); // BSON requires that the top level object be a document, but an array looks close enough to a document that we can pretend it is one reader.ReadStartDocument(); diff --git a/src/MongoDB.Driver/Core/Operations/DatabaseExistsOperation.cs b/src/MongoDB.Driver/Core/Operations/DatabaseExistsOperation.cs index 9b6f4cdca4d..653a04543b3 100644 --- a/src/MongoDB.Driver/Core/Operations/DatabaseExistsOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/DatabaseExistsOperation.cs @@ -15,6 +15,7 @@ using System.Linq; using System.Threading.Tasks; +using MongoDB.Bson.Serialization; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; @@ -26,13 +27,22 @@ internal sealed class DatabaseExistsOperation : IReadOperation private DatabaseNamespace _databaseNamespace; private MessageEncoderSettings _messageEncoderSettings; private bool _retryRequested; + private IBsonSerializationDomain _serializationDomain; - public DatabaseExistsOperation(DatabaseNamespace databaseNamespace, MessageEncoderSettings messageEncoderSettings) + public DatabaseExistsOperation(DatabaseNamespace databaseNamespace, MessageEncoderSettings messageEncoderSettings, IBsonSerializationDomain serializationDomain) { _databaseNamespace = Ensure.IsNotNull(databaseNamespace, nameof(databaseNamespace)); _messageEncoderSettings = Ensure.IsNotNull(messageEncoderSettings, nameof(messageEncoderSettings)); + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); + } + + // EXIT + public DatabaseExistsOperation(DatabaseNamespace databaseNamespace, MessageEncoderSettings messageEncoderSettings) + : this(databaseNamespace, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain) + { } + public DatabaseNamespace DatabaseNamespace { get { return _databaseNamespace; } @@ -71,7 +81,7 @@ public async Task ExecuteAsync(OperationContext operationContext, IReadBin private ListDatabasesOperation CreateOperation() { - return new ListDatabasesOperation(_messageEncoderSettings) + return new ListDatabasesOperation(_messageEncoderSettings, _serializationDomain) { RetryRequested = _retryRequested }; diff --git a/src/MongoDB.Driver/Core/Operations/DistinctOperation.cs b/src/MongoDB.Driver/Core/Operations/DistinctOperation.cs index 0e25eb3d097..52c7d9bddcd 100644 --- a/src/MongoDB.Driver/Core/Operations/DistinctOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/DistinctOperation.cs @@ -38,14 +38,29 @@ internal sealed class DistinctOperation : IReadOperation _valueSerializer; - public DistinctOperation(CollectionNamespace collectionNamespace, IBsonSerializer valueSerializer, string fieldName, MessageEncoderSettings messageEncoderSettings) + public DistinctOperation(CollectionNamespace collectionNamespace, + IBsonSerializer valueSerializer, + string fieldName, + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) { _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); _valueSerializer = Ensure.IsNotNull(valueSerializer, nameof(valueSerializer)); _fieldName = Ensure.IsNotNullOrEmpty(fieldName, nameof(fieldName)); _messageEncoderSettings = Ensure.IsNotNull(messageEncoderSettings, nameof(messageEncoderSettings)); + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); + } + + //EXIT + public DistinctOperation(CollectionNamespace collectionNamespace, + IBsonSerializer valueSerializer, + string fieldName, + MessageEncoderSettings messageEncoderSettings) + : this(collectionNamespace, valueSerializer, fieldName, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain) + { } public Collation Collation @@ -158,7 +173,7 @@ private ReadCommandOperation CreateOperation(RetryableReadContex var command = CreateCommand(context.Channel.ConnectionDescription, context.Binding.Session); var serializer = new DistinctResultDeserializer(_valueSerializer); - return new ReadCommandOperation(_collectionNamespace.DatabaseNamespace, command, serializer, _messageEncoderSettings) + return new ReadCommandOperation(_collectionNamespace.DatabaseNamespace, command, serializer, _messageEncoderSettings, _serializationDomain) { RetryRequested = _retryRequested // might be overridden by retryable read context }; diff --git a/src/MongoDB.Driver/Core/Operations/DropCollectionOperation.cs b/src/MongoDB.Driver/Core/Operations/DropCollectionOperation.cs index d667367575a..1325944560a 100644 --- a/src/MongoDB.Driver/Core/Operations/DropCollectionOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/DropCollectionOperation.cs @@ -16,6 +16,7 @@ using System; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Events; @@ -34,9 +35,10 @@ public static IWriteOperation CreateEncryptedDropCollectionOperati CollectionNamespace collectionNamespace, BsonDocument encryptedFields, MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain, Action configureDropCollectionConfigurator) { - var mainOperation = new DropCollectionOperation(collectionNamespace, messageEncoderSettings) + var mainOperation = new DropCollectionOperation(collectionNamespace, messageEncoderSettings, serializationDomain) { EncryptedFields = encryptedFields }; @@ -56,21 +58,46 @@ public static IWriteOperation CreateEncryptedDropCollectionOperati } DropCollectionOperation CreateInnerDropOperation(string collectionName) - => new DropCollectionOperation(new CollectionNamespace(collectionNamespace.DatabaseNamespace.DatabaseName, collectionName), messageEncoderSettings); + => new DropCollectionOperation(new CollectionNamespace(collectionNamespace.DatabaseNamespace.DatabaseName, collectionName), messageEncoderSettings, serializationDomain); } + + //EXIT + public static IWriteOperation CreateEncryptedDropCollectionOperationIfConfigured( + CollectionNamespace collectionNamespace, + BsonDocument encryptedFields, + MessageEncoderSettings messageEncoderSettings, + Action configureDropCollectionConfigurator) + => CreateEncryptedDropCollectionOperationIfConfigured( + collectionNamespace, + encryptedFields, + messageEncoderSettings, + BsonSerializer.DefaultSerializationDomain, + configureDropCollectionConfigurator); + #endregion private readonly CollectionNamespace _collectionNamespace; private BsonDocument _encryptedFields; private readonly MessageEncoderSettings _messageEncoderSettings; + private readonly IBsonSerializationDomain _serializationDomain; private WriteConcern _writeConcern; public DropCollectionOperation( CollectionNamespace collectionNamespace, - MessageEncoderSettings messageEncoderSettings) + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) { _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); _messageEncoderSettings = messageEncoderSettings; + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); + } + + //EXIT + public DropCollectionOperation( + CollectionNamespace collectionNamespace, + MessageEncoderSettings messageEncoderSettings) + : this(collectionNamespace, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain) + { } public CollectionNamespace CollectionNamespace @@ -164,7 +191,7 @@ internal BsonDocument CreateCommand(ICoreSessionHandle session) private WriteCommandOperation CreateOperation(ICoreSessionHandle session) { var command = CreateCommand(session); - return new WriteCommandOperation(_collectionNamespace.DatabaseNamespace, command, BsonDocumentSerializer.Instance, _messageEncoderSettings); + return new WriteCommandOperation(_collectionNamespace.DatabaseNamespace, command, BsonDocumentSerializer.Instance, _messageEncoderSettings, _serializationDomain); } private bool ShouldIgnoreException(MongoCommandException ex) diff --git a/src/MongoDB.Driver/Core/Operations/DropDatabaseOperation.cs b/src/MongoDB.Driver/Core/Operations/DropDatabaseOperation.cs index 39be674d49c..9a4f0986857 100644 --- a/src/MongoDB.Driver/Core/Operations/DropDatabaseOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/DropDatabaseOperation.cs @@ -16,6 +16,7 @@ using System; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Events; @@ -28,14 +29,25 @@ internal sealed class DropDatabaseOperation : IWriteOperation { private readonly DatabaseNamespace _databaseNamespace; private readonly MessageEncoderSettings _messageEncoderSettings; + private readonly IBsonSerializationDomain _serializationDomain; private WriteConcern _writeConcern; public DropDatabaseOperation( DatabaseNamespace databaseNamespace, - MessageEncoderSettings messageEncoderSettings) + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) { _databaseNamespace = Ensure.IsNotNull(databaseNamespace, nameof(databaseNamespace)); _messageEncoderSettings = messageEncoderSettings; + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); + } + + //EXIT + public DropDatabaseOperation( + DatabaseNamespace databaseNamespace, + MessageEncoderSettings messageEncoderSettings) + : this(databaseNamespace, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain) + { } public DatabaseNamespace DatabaseNamespace @@ -97,7 +109,7 @@ public async Task ExecuteAsync(OperationContext operationContext, private WriteCommandOperation CreateOperation(ICoreSessionHandle session) { var command = CreateCommand(session); - return new WriteCommandOperation(_databaseNamespace, command, BsonDocumentSerializer.Instance, _messageEncoderSettings); + return new WriteCommandOperation(_databaseNamespace, command, BsonDocumentSerializer.Instance, _messageEncoderSettings, _serializationDomain); } } } diff --git a/src/MongoDB.Driver/Core/Operations/DropIndexOperation.cs b/src/MongoDB.Driver/Core/Operations/DropIndexOperation.cs index ef68ad071c5..6142ba86a47 100644 --- a/src/MongoDB.Driver/Core/Operations/DropIndexOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/DropIndexOperation.cs @@ -16,6 +16,7 @@ using System; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Events; @@ -32,23 +33,44 @@ internal sealed class DropIndexOperation : IWriteOperation private TimeSpan? _maxTime; private readonly MessageEncoderSettings _messageEncoderSettings; private WriteConcern _writeConcern; + private readonly IBsonSerializationDomain _serializationDomain; + public DropIndexOperation( + CollectionNamespace collectionNamespace, + BsonDocument keys, + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) + : this(collectionNamespace, IndexNameHelper.GetIndexName(keys), messageEncoderSettings, serializationDomain) + { + } + + //EXIT public DropIndexOperation( CollectionNamespace collectionNamespace, BsonDocument keys, MessageEncoderSettings messageEncoderSettings) - : this(collectionNamespace, IndexNameHelper.GetIndexName(keys), messageEncoderSettings) + : this(collectionNamespace, keys, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain) { } public DropIndexOperation( CollectionNamespace collectionNamespace, string indexName, - MessageEncoderSettings messageEncoderSettings) + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) { _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); _indexName = Ensure.IsNotNullOrEmpty(indexName, nameof(indexName)); _messageEncoderSettings = Ensure.IsNotNull(messageEncoderSettings, nameof(messageEncoderSettings)); + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); + } + + public DropIndexOperation( + CollectionNamespace collectionNamespace, + string indexName, + MessageEncoderSettings messageEncoderSettings) + : this(collectionNamespace, indexName, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain) + { } public CollectionNamespace CollectionNamespace @@ -156,7 +178,7 @@ public async Task ExecuteAsync(OperationContext operationContext, private WriteCommandOperation CreateOperation(ICoreSessionHandle session) { var command = CreateCommand(session); - return new WriteCommandOperation(_collectionNamespace.DatabaseNamespace, command, BsonDocumentSerializer.Instance, _messageEncoderSettings); + return new WriteCommandOperation(_collectionNamespace.DatabaseNamespace, command, BsonDocumentSerializer.Instance, _messageEncoderSettings, _serializationDomain); } private bool ShouldIgnoreException(MongoCommandException ex) diff --git a/src/MongoDB.Driver/Core/Operations/DropSearchIndexOperation.cs b/src/MongoDB.Driver/Core/Operations/DropSearchIndexOperation.cs index aff890be381..79ed98a33e4 100644 --- a/src/MongoDB.Driver/Core/Operations/DropSearchIndexOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/DropSearchIndexOperation.cs @@ -15,6 +15,7 @@ using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Events; @@ -32,6 +33,7 @@ internal sealed class DropSearchIndexOperation : IWriteOperation private readonly CollectionNamespace _collectionNamespace; private readonly string _indexName; private readonly MessageEncoderSettings _messageEncoderSettings; + private readonly IBsonSerializationDomain _serializationDomain; // constructors /// @@ -40,14 +42,17 @@ internal sealed class DropSearchIndexOperation : IWriteOperation /// The collection namespace. /// The name of the index. /// The message encoder settings. + /// The serialization domain. public DropSearchIndexOperation( CollectionNamespace collectionNamespace, string indexName, - MessageEncoderSettings messageEncoderSettings) + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) { _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); _indexName = Ensure.IsNotNullOrEmpty(indexName, nameof(indexName)); _messageEncoderSettings = Ensure.IsNotNull(messageEncoderSettings, nameof(messageEncoderSettings)); + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); } // methods @@ -59,7 +64,7 @@ private BsonDocument CreateCommand() => }; private WriteCommandOperation CreateOperation() => - new(_collectionNamespace.DatabaseNamespace, CreateCommand(), BsonDocumentSerializer.Instance, _messageEncoderSettings); + new(_collectionNamespace.DatabaseNamespace, CreateCommand(), BsonDocumentSerializer.Instance, _messageEncoderSettings, _serializationDomain); /// public BsonDocument Execute(OperationContext operationContext, IWriteBinding binding) diff --git a/src/MongoDB.Driver/Core/Operations/EndTransactionOperation.cs b/src/MongoDB.Driver/Core/Operations/EndTransactionOperation.cs index 0544b2498c3..9adc8278e4a 100644 --- a/src/MongoDB.Driver/Core/Operations/EndTransactionOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/EndTransactionOperation.cs @@ -16,6 +16,7 @@ using System; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Misc; @@ -28,15 +29,29 @@ internal abstract class EndTransactionOperation : IReadOperation private MessageEncoderSettings _messageEncoderSettings; private readonly BsonDocument _recoveryToken; private readonly WriteConcern _writeConcern; + private IBsonSerializationDomain _serializationDomain; - protected EndTransactionOperation(BsonDocument recoveryToken, WriteConcern writeConcern) + protected EndTransactionOperation(BsonDocument recoveryToken, WriteConcern writeConcern, IBsonSerializationDomain serializationDomain) { _recoveryToken = recoveryToken; _writeConcern = Ensure.IsNotNull(writeConcern, nameof(writeConcern)); + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); + } + + //EXIT + protected EndTransactionOperation(BsonDocument recoveryToken, WriteConcern writeConcern) + : this(recoveryToken, writeConcern, BsonSerializer.DefaultSerializationDomain) + { } + protected EndTransactionOperation(WriteConcern writeConcern, IBsonSerializationDomain serializationDomain) + : this(recoveryToken: null, writeConcern, serializationDomain) + { + } + + //EXIT protected EndTransactionOperation(WriteConcern writeConcern) - : this(recoveryToken: null, writeConcern) + : this(writeConcern, BsonSerializer.DefaultSerializationDomain) { } @@ -89,7 +104,7 @@ protected virtual BsonDocument CreateCommand() private IReadOperation CreateOperation() { var command = CreateCommand(); - return new ReadCommandOperation(DatabaseNamespace.Admin, command, BsonDocumentSerializer.Instance, _messageEncoderSettings) + return new ReadCommandOperation(DatabaseNamespace.Admin, command, BsonDocumentSerializer.Instance, _messageEncoderSettings, _serializationDomain) { RetryRequested = false }; @@ -98,13 +113,25 @@ private IReadOperation CreateOperation() internal sealed class AbortTransactionOperation : EndTransactionOperation { + public AbortTransactionOperation(BsonDocument recoveryToken, WriteConcern writeConcern, IBsonSerializationDomain serializationDomain) + : base(recoveryToken, writeConcern, serializationDomain) + { + } + + //EXIT public AbortTransactionOperation(BsonDocument recoveryToken, WriteConcern writeConcern) - : base(recoveryToken, writeConcern) + : this(recoveryToken, writeConcern, BsonSerializer.DefaultSerializationDomain) { } + public AbortTransactionOperation(WriteConcern writeConcern, IBsonSerializationDomain serializationDomain) + : base(writeConcern, serializationDomain) + { + } + + //EXIT public AbortTransactionOperation(WriteConcern writeConcern) - : base(writeConcern) + : this(writeConcern, BsonSerializer.DefaultSerializationDomain) { } @@ -115,16 +142,29 @@ internal sealed class CommitTransactionOperation : EndTransactionOperation { private TimeSpan? _maxCommitTime; + public CommitTransactionOperation(WriteConcern writeConcern, IBsonSerializationDomain serializationDomain) + : base(writeConcern, serializationDomain) + { + } + + //EXIT public CommitTransactionOperation(WriteConcern writeConcern) - : base(writeConcern) + : this(writeConcern, BsonSerializer.DefaultSerializationDomain) { } + public CommitTransactionOperation(BsonDocument recoveryToken, WriteConcern writeConcern, IBsonSerializationDomain serializationDomain) + : base(recoveryToken, writeConcern, serializationDomain) + { + } + + //EXIT public CommitTransactionOperation(BsonDocument recoveryToken, WriteConcern writeConcern) - : base(recoveryToken, writeConcern) + : this(recoveryToken, writeConcern, BsonSerializer.DefaultSerializationDomain) { } + public TimeSpan? MaxCommitTime { get => _maxCommitTime; diff --git a/src/MongoDB.Driver/Core/Operations/EstimatedDocumentCountOperation.cs b/src/MongoDB.Driver/Core/Operations/EstimatedDocumentCountOperation.cs index eac2d1a32a1..9f74b4bd75a 100644 --- a/src/MongoDB.Driver/Core/Operations/EstimatedDocumentCountOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/EstimatedDocumentCountOperation.cs @@ -16,6 +16,7 @@ using System; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Events; using MongoDB.Driver.Core.Misc; @@ -31,11 +32,19 @@ internal sealed class EstimatedDocumentCountOperation : IReadOperation private readonly MessageEncoderSettings _messageEncoderSettings; private ReadConcern _readConcern = ReadConcern.Default; private bool _retryRequested; + private readonly IBsonSerializationDomain _serializationDomain; - public EstimatedDocumentCountOperation(CollectionNamespace collectionNamespace, MessageEncoderSettings messageEncoderSettings) + public EstimatedDocumentCountOperation(CollectionNamespace collectionNamespace, MessageEncoderSettings messageEncoderSettings, IBsonSerializationDomain serializationDomain) { _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); _messageEncoderSettings = Ensure.IsNotNull(messageEncoderSettings, nameof(messageEncoderSettings)); + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); + } + + //EXIT + public EstimatedDocumentCountOperation(CollectionNamespace collectionNamespace, MessageEncoderSettings messageEncoderSettings) + : this(collectionNamespace, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain) + { } public CollectionNamespace CollectionNamespace => _collectionNamespace; @@ -96,7 +105,7 @@ public async Task ExecuteAsync(OperationContext operationContext, IReadBin private IExecutableInRetryableReadContext CreateCountOperation() { - var countOperation = new CountOperation(_collectionNamespace, _messageEncoderSettings) + var countOperation = new CountOperation(_collectionNamespace, _messageEncoderSettings, _serializationDomain) { Comment = _comment, MaxTime = _maxTime, diff --git a/src/MongoDB.Driver/Core/Operations/FindAndModifyOperationBase.cs b/src/MongoDB.Driver/Core/Operations/FindAndModifyOperationBase.cs index d32198f81af..ac2ef99bf5b 100644 --- a/src/MongoDB.Driver/Core/Operations/FindAndModifyOperationBase.cs +++ b/src/MongoDB.Driver/Core/Operations/FindAndModifyOperationBase.cs @@ -37,12 +37,17 @@ internal abstract class FindAndModifyOperationBase : IWriteOperation _resultSerializer; private WriteConcern _writeConcern; private bool _retryRequested; + private readonly IBsonSerializationDomain _serializationDomain; - public FindAndModifyOperationBase(CollectionNamespace collectionNamespace, IBsonSerializer resultSerializer, MessageEncoderSettings messageEncoderSettings) + public FindAndModifyOperationBase(CollectionNamespace collectionNamespace, + IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) { _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); _resultSerializer = Ensure.IsNotNull(resultSerializer, nameof(resultSerializer)); _messageEncoderSettings = Ensure.IsNotNull(messageEncoderSettings, nameof(messageEncoderSettings)); + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); } public Collation Collation @@ -157,7 +162,7 @@ public async Task ExecuteAttemptAsync(OperationContext operationContext private WriteCommandOperation CreateOperation(ICoreSessionHandle session, ConnectionDescription connectionDescription, long? transactionNumber) { var command = CreateCommand(session, connectionDescription, transactionNumber); - return new WriteCommandOperation(_collectionNamespace.DatabaseNamespace, command, RawBsonDocumentSerializer.Instance, _messageEncoderSettings) + return new WriteCommandOperation(_collectionNamespace.DatabaseNamespace, command, RawBsonDocumentSerializer.Instance, _messageEncoderSettings, _serializationDomain) { CommandValidator = GetCommandValidator() }; @@ -174,7 +179,7 @@ private TResult ProcessCommandResult(ConnectionId connectionId, RawBsonDocument using (var stream = new ByteBufferStream(rawBsonDocument.Slice, ownsBuffer: false)) using (var reader = new BsonBinaryReader(stream, binaryReaderSettings)) { - var context = BsonDeserializationContext.CreateRoot(reader); + var context = BsonDeserializationContext.CreateRoot(reader, _serializationDomain); return _resultSerializer.Deserialize(context); } } diff --git a/src/MongoDB.Driver/Core/Operations/FindOneAndDeleteOperation.cs b/src/MongoDB.Driver/Core/Operations/FindOneAndDeleteOperation.cs index a06756a1a6f..f1e7a773459 100644 --- a/src/MongoDB.Driver/Core/Operations/FindOneAndDeleteOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/FindOneAndDeleteOperation.cs @@ -33,12 +33,23 @@ internal sealed class FindOneAndDeleteOperation : FindAndModifyOperatio private BsonDocument _projection; private BsonDocument _sort; - public FindOneAndDeleteOperation(CollectionNamespace collectionNamespace, BsonDocument filter, IBsonSerializer resultSerializer, MessageEncoderSettings messageEncoderSettings) - : base(collectionNamespace, resultSerializer, messageEncoderSettings) + public FindOneAndDeleteOperation(CollectionNamespace collectionNamespace, + BsonDocument filter, IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) + : base(collectionNamespace, resultSerializer, messageEncoderSettings, serializationDomain) { _filter = Ensure.IsNotNull(filter, nameof(filter)); } + //EXIT + public FindOneAndDeleteOperation(CollectionNamespace collectionNamespace, + BsonDocument filter, IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings) + : this(collectionNamespace, filter, resultSerializer, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain) + { + } + public BsonDocument Filter { get { return _filter; } diff --git a/src/MongoDB.Driver/Core/Operations/FindOneAndReplaceOperation.cs b/src/MongoDB.Driver/Core/Operations/FindOneAndReplaceOperation.cs index f7aaa59e809..d86ef41ac64 100644 --- a/src/MongoDB.Driver/Core/Operations/FindOneAndReplaceOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/FindOneAndReplaceOperation.cs @@ -37,14 +37,30 @@ internal sealed class FindOneAndReplaceOperation : FindAndModifyOperati private ReturnDocument _returnDocument; private BsonDocument _sort; - public FindOneAndReplaceOperation(CollectionNamespace collectionNamespace, BsonDocument filter, BsonDocument replacement, IBsonSerializer resultSerializer, MessageEncoderSettings messageEncoderSettings) - : base(collectionNamespace, resultSerializer, messageEncoderSettings) + public FindOneAndReplaceOperation(CollectionNamespace collectionNamespace, + BsonDocument filter, + BsonDocument replacement, + IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) + : base(collectionNamespace, resultSerializer, messageEncoderSettings, serializationDomain) { _filter = Ensure.IsNotNull(filter, nameof(filter)); _replacement = Ensure.IsNotNull(replacement, nameof(replacement)); _returnDocument = ReturnDocument.Before; } + //EXIT + public FindOneAndReplaceOperation(CollectionNamespace collectionNamespace, + BsonDocument filter, + BsonDocument replacement, + IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings) + : this(collectionNamespace, filter, replacement, resultSerializer, messageEncoderSettings, + BsonSerializer.DefaultSerializationDomain) + { + } + public bool? BypassDocumentValidation { get { return _bypassDocumentValidation; } diff --git a/src/MongoDB.Driver/Core/Operations/FindOneAndUpdateOperation.cs b/src/MongoDB.Driver/Core/Operations/FindOneAndUpdateOperation.cs index 42f1e1702d2..7c25b9edaab 100644 --- a/src/MongoDB.Driver/Core/Operations/FindOneAndUpdateOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/FindOneAndUpdateOperation.cs @@ -40,14 +40,27 @@ internal sealed class FindOneAndUpdateOperation : FindAndModifyOperatio private BsonDocument _sort; private readonly BsonValue _update; - public FindOneAndUpdateOperation(CollectionNamespace collectionNamespace, BsonDocument filter, BsonValue update, IBsonSerializer resultSerializer, MessageEncoderSettings messageEncoderSettings) - : base(collectionNamespace, resultSerializer, messageEncoderSettings) + public FindOneAndUpdateOperation(CollectionNamespace collectionNamespace, + BsonDocument filter, BsonValue update, + IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) + : base(collectionNamespace, resultSerializer, messageEncoderSettings, serializationDomain) { _filter = Ensure.IsNotNull(filter, nameof(filter)); _update = EnsureUpdateIsValid(update); _returnDocument = ReturnDocument.Before; } + //EXIT + public FindOneAndUpdateOperation(CollectionNamespace collectionNamespace, + BsonDocument filter, BsonValue update, + IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings) + : this(collectionNamespace, filter, update, resultSerializer, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain) + { + } + public IEnumerable ArrayFilters { get { return _arrayFilters; } diff --git a/src/MongoDB.Driver/Core/Operations/FindOperation.cs b/src/MongoDB.Driver/Core/Operations/FindOperation.cs index 7ebc524b81e..dd0df0cb619 100644 --- a/src/MongoDB.Driver/Core/Operations/FindOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/FindOperation.cs @@ -62,6 +62,7 @@ internal sealed class FindOperation : IReadOperation _resultSerializer; private bool _retryRequested; private bool? _returnKey; + private IBsonSerializationDomain _serializationDomain; private bool? _showRecordId; private bool? _singleBatch; private int? _skip; @@ -70,14 +71,26 @@ internal sealed class FindOperation : IReadOperation resultSerializer, - MessageEncoderSettings messageEncoderSettings) + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) { _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); _resultSerializer = Ensure.IsNotNull(resultSerializer, nameof(resultSerializer)); _messageEncoderSettings = Ensure.IsNotNull(messageEncoderSettings, nameof(messageEncoderSettings)); + _serializationDomain = serializationDomain ?? BsonSerializer.DefaultSerializationDomain; _cursorType = CursorType.NonTailable; } + //EXIT + public FindOperation( + CollectionNamespace collectionNamespace, + IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings) + : this(collectionNamespace, resultSerializer, messageEncoderSettings, + BsonSerializer.DefaultSerializationDomain) + { + } + public bool? AllowDiskUse { get { return _allowDiskUse; } @@ -359,6 +372,7 @@ private AsyncCursor CreateCursor(IChannelSourceHandle channelSource, _limit < 0 ? Math.Abs(_limit.Value) : _limit, _resultSerializer, _messageEncoderSettings, + _serializationDomain, _cursorType == CursorType.TailableAwait ? _maxAwaitTime : null); } @@ -369,7 +383,7 @@ private CursorBatch CreateFirstCursorBatch(BsonDocument cursorDocumen using (batch) { - var documents = CursorBatchDeserializationHelper.DeserializeBatch(batch, _resultSerializer, _messageEncoderSettings); + var documents = CursorBatchDeserializationHelper.DeserializeBatch(batch, _resultSerializer, _messageEncoderSettings, _serializationDomain); return new CursorBatch(cursorId, documents); } } @@ -383,7 +397,8 @@ private ReadCommandOperation CreateOperation(RetryableReadContext _collectionNamespace.DatabaseNamespace, command, __findCommandResultSerializer, - _messageEncoderSettings) + _messageEncoderSettings, + _serializationDomain) { RetryRequested = _retryRequested // might be overridden by retryable read context }; diff --git a/src/MongoDB.Driver/Core/Operations/ListCollectionsOperation.cs b/src/MongoDB.Driver/Core/Operations/ListCollectionsOperation.cs index 320c1c3ea71..7e9913b8624 100644 --- a/src/MongoDB.Driver/Core/Operations/ListCollectionsOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/ListCollectionsOperation.cs @@ -17,6 +17,7 @@ using System.Linq; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Events; @@ -35,13 +36,24 @@ internal sealed class ListCollectionsOperation : IReadOperation CreateOperation() { "authorizedCollections", () => _authorizedCollections.Value, _authorizedCollections.HasValue }, { "comment", _comment, _comment != null } }; - return new ReadCommandOperation(_databaseNamespace, command, BsonDocumentSerializer.Instance, _messageEncoderSettings) + return new ReadCommandOperation(_databaseNamespace, command, BsonDocumentSerializer.Instance, _messageEncoderSettings, _serializationDomain) { RetryRequested = _retryRequested // might be overridden by retryable read context }; @@ -173,7 +185,8 @@ private IAsyncCursor CreateCursor(IChannelSourceHandle channelSour batchSize: _batchSize ?? 0, 0, BsonDocumentSerializer.Instance, - _messageEncoderSettings); + _messageEncoderSettings, + _serializationDomain); return cursor; } diff --git a/src/MongoDB.Driver/Core/Operations/ListDatabasesOperation.cs b/src/MongoDB.Driver/Core/Operations/ListDatabasesOperation.cs index e55151e5b5c..81fe21a4690 100644 --- a/src/MongoDB.Driver/Core/Operations/ListDatabasesOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/ListDatabasesOperation.cs @@ -17,6 +17,7 @@ using System.Linq; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Events; @@ -33,12 +34,21 @@ internal sealed class ListDatabasesOperation : IReadOperation CreateCursor(BsonDocument reply) private ReadCommandOperation CreateOperation() { var command = CreateCommand(); - return new ReadCommandOperation(DatabaseNamespace.Admin, command, BsonDocumentSerializer.Instance, _messageEncoderSettings) + return new ReadCommandOperation(DatabaseNamespace.Admin, command, BsonDocumentSerializer.Instance, _messageEncoderSettings, _serializationDomain) { RetryRequested = _retryRequested }; diff --git a/src/MongoDB.Driver/Core/Operations/ListIndexesOperation.cs b/src/MongoDB.Driver/Core/Operations/ListIndexesOperation.cs index b91633c4716..3c3777db58d 100644 --- a/src/MongoDB.Driver/Core/Operations/ListIndexesOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/ListIndexesOperation.cs @@ -16,6 +16,7 @@ using System; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Events; using MongoDB.Driver.Core.Misc; @@ -30,13 +31,24 @@ internal sealed class ListIndexesOperation : IReadOperation> ExecuteAsync(OperationContext oper private IExecutableInRetryableReadContext> CreateOperation() { - return new ListIndexesUsingCommandOperation(_collectionNamespace, _messageEncoderSettings) + return new ListIndexesUsingCommandOperation(_collectionNamespace, _messageEncoderSettings, _serializationDomain) { BatchSize = _batchSize, Comment = _comment, diff --git a/src/MongoDB.Driver/Core/Operations/ListIndexesUsingCommandOperation.cs b/src/MongoDB.Driver/Core/Operations/ListIndexesUsingCommandOperation.cs index 290bbbf00e5..3607b973d25 100644 --- a/src/MongoDB.Driver/Core/Operations/ListIndexesUsingCommandOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/ListIndexesUsingCommandOperation.cs @@ -17,6 +17,7 @@ using System.Linq; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Events; @@ -32,13 +33,24 @@ internal sealed class ListIndexesUsingCommandOperation : IReadOperation CreateOperation() { "cursor", () => new BsonDocument("batchSize", _batchSize.Value), _batchSize.HasValue }, { "comment", _comment, _comment != null }, }; - return new ReadCommandOperation(databaseNamespace, command, BsonDocumentSerializer.Instance, _messageEncoderSettings) + return new ReadCommandOperation(databaseNamespace, command, BsonDocumentSerializer.Instance, _messageEncoderSettings, _serializationDomain) { RetryRequested = _retryRequested // might be overridden by retryable read context }; @@ -156,7 +168,8 @@ private IAsyncCursor CreateCursor(IChannelSourceHandle channelSour batchSize: _batchSize ?? 0, 0, BsonDocumentSerializer.Instance, - _messageEncoderSettings); + _messageEncoderSettings, + _serializationDomain); return cursor; } diff --git a/src/MongoDB.Driver/Core/Operations/MapReduceOperation.cs b/src/MongoDB.Driver/Core/Operations/MapReduceOperation.cs index a68b2227278..c9648cce8c8 100644 --- a/src/MongoDB.Driver/Core/Operations/MapReduceOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/MapReduceOperation.cs @@ -35,6 +35,7 @@ internal sealed class MapReduceOperation : MapReduceOperationBase, IRea // fields private ReadConcern _readConcern = ReadConcern.Default; private readonly IBsonSerializer _resultSerializer; + private readonly IBsonSerializationDomain _serializationDomain; // constructors /// @@ -45,7 +46,12 @@ internal sealed class MapReduceOperation : MapReduceOperationBase, IRea /// The reduce function. /// The result serializer. /// The message encoder settings. - public MapReduceOperation(CollectionNamespace collectionNamespace, BsonJavaScript mapFunction, BsonJavaScript reduceFunction, IBsonSerializer resultSerializer, MessageEncoderSettings messageEncoderSettings) + /// The serialization domain. + public MapReduceOperation(CollectionNamespace collectionNamespace, + BsonJavaScript mapFunction, BsonJavaScript reduceFunction, + IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) : base( collectionNamespace, mapFunction, @@ -53,6 +59,22 @@ public MapReduceOperation(CollectionNamespace collectionNamespace, BsonJavaScrip messageEncoderSettings) { _resultSerializer = Ensure.IsNotNull(resultSerializer, nameof(resultSerializer)); + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); + } + + //EXIT + public MapReduceOperation(CollectionNamespace collectionNamespace, + BsonJavaScript mapFunction, BsonJavaScript reduceFunction, + IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings) + : this( + collectionNamespace, + mapFunction, + reduceFunction, + resultSerializer, + messageEncoderSettings, + BsonSerializer.DefaultSerializationDomain) + { } // properties @@ -135,7 +157,7 @@ private ReadCommandOperation CreateOperation(ICoreSessionHandle sessi var command = CreateCommand(session, connectionDescription); var resultArraySerializer = new ArraySerializer(_resultSerializer); var resultSerializer = new ElementDeserializer("results", resultArraySerializer); - return new ReadCommandOperation(CollectionNamespace.DatabaseNamespace, command, resultSerializer, MessageEncoderSettings) + return new ReadCommandOperation(CollectionNamespace.DatabaseNamespace, command, resultSerializer, MessageEncoderSettings, _serializationDomain) { RetryRequested = false }; diff --git a/src/MongoDB.Driver/Core/Operations/MapReduceOutputToCollectionOperation.cs b/src/MongoDB.Driver/Core/Operations/MapReduceOutputToCollectionOperation.cs index 99e32b1adcd..1eb0e24c8e5 100644 --- a/src/MongoDB.Driver/Core/Operations/MapReduceOutputToCollectionOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/MapReduceOutputToCollectionOperation.cs @@ -16,6 +16,7 @@ using System; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Connections; @@ -35,6 +36,7 @@ internal sealed class MapReduceOutputToCollectionOperation : MapReduceOperationB private bool? _nonAtomicOutput; private readonly CollectionNamespace _outputCollectionNamespace; private MapReduceOutputMode _outputMode; + private IBsonSerializationDomain _serializationDomain; private bool? _shardedOutput; private WriteConcern _writeConcern; @@ -47,12 +49,14 @@ internal sealed class MapReduceOutputToCollectionOperation : MapReduceOperationB /// The map function. /// The reduce function. /// The message encoder settings. + /// The serialization domain public MapReduceOutputToCollectionOperation( CollectionNamespace collectionNamespace, CollectionNamespace outputCollectionNamespace, BsonJavaScript mapFunction, BsonJavaScript reduceFunction, - MessageEncoderSettings messageEncoderSettings) + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) : base( collectionNamespace, mapFunction, @@ -60,9 +64,27 @@ public MapReduceOutputToCollectionOperation( messageEncoderSettings) { _outputCollectionNamespace = Ensure.IsNotNull(outputCollectionNamespace, nameof(outputCollectionNamespace)); + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); _outputMode = MapReduceOutputMode.Replace; } + //EXIT + public MapReduceOutputToCollectionOperation( + CollectionNamespace collectionNamespace, + CollectionNamespace outputCollectionNamespace, + BsonJavaScript mapFunction, + BsonJavaScript reduceFunction, + MessageEncoderSettings messageEncoderSettings) + : this( + collectionNamespace, + outputCollectionNamespace, + mapFunction, + reduceFunction, + messageEncoderSettings, + BsonSerializer.DefaultSerializationDomain) + { + } + // properties /// /// Gets or sets a value indicating whether to bypass document validation. @@ -199,7 +221,7 @@ public async Task ExecuteAsync(OperationContext operationContext, private WriteCommandOperation CreateOperation(ICoreSessionHandle session, ConnectionDescription connectionDescription) { var command = CreateCommand(session, connectionDescription); - return new WriteCommandOperation(CollectionNamespace.DatabaseNamespace, command, BsonDocumentSerializer.Instance, MessageEncoderSettings); + return new WriteCommandOperation(CollectionNamespace.DatabaseNamespace, command, BsonDocumentSerializer.Instance, MessageEncoderSettings, _serializationDomain); } } } diff --git a/src/MongoDB.Driver/Core/Operations/ReadCommandOperation.cs b/src/MongoDB.Driver/Core/Operations/ReadCommandOperation.cs index 711d31c37df..88ce4c20a2b 100644 --- a/src/MongoDB.Driver/Core/Operations/ReadCommandOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/ReadCommandOperation.cs @@ -27,12 +27,24 @@ internal sealed class ReadCommandOperation : CommandOperationBas { private bool _retryRequested; + public ReadCommandOperation( + DatabaseNamespace databaseNamespace, + BsonDocument command, + IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) + : base(databaseNamespace, command, resultSerializer, messageEncoderSettings, serializationDomain) + { + } + + //EXIT public ReadCommandOperation( DatabaseNamespace databaseNamespace, BsonDocument command, IBsonSerializer resultSerializer, MessageEncoderSettings messageEncoderSettings) - : base(databaseNamespace, command, resultSerializer, messageEncoderSettings) + : this(databaseNamespace, command, resultSerializer, messageEncoderSettings, + BsonSerializer.DefaultSerializationDomain) { } diff --git a/src/MongoDB.Driver/Core/Operations/RenameCollectionOperation.cs b/src/MongoDB.Driver/Core/Operations/RenameCollectionOperation.cs index aee3f2fed1c..ef61a742ba1 100644 --- a/src/MongoDB.Driver/Core/Operations/RenameCollectionOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/RenameCollectionOperation.cs @@ -16,6 +16,7 @@ using System; using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Connections; @@ -31,16 +32,29 @@ internal sealed class RenameCollectionOperation : IWriteOperation private bool? _dropTarget; private readonly MessageEncoderSettings _messageEncoderSettings; private readonly CollectionNamespace _newCollectionNamespace; + private readonly IBsonSerializationDomain _serializationDomain; private WriteConcern _writeConcern; public RenameCollectionOperation( CollectionNamespace collectionNamespace, CollectionNamespace newCollectionNamespace, - MessageEncoderSettings messageEncoderSettings) + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) { _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); _newCollectionNamespace = Ensure.IsNotNull(newCollectionNamespace, nameof(newCollectionNamespace)); _messageEncoderSettings = messageEncoderSettings; + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); + } + + //EXIT + public RenameCollectionOperation( + CollectionNamespace collectionNamespace, + CollectionNamespace newCollectionNamespace, + MessageEncoderSettings messageEncoderSettings) + : this(collectionNamespace, newCollectionNamespace, messageEncoderSettings, + BsonSerializer.DefaultSerializationDomain) + { } public CollectionNamespace CollectionNamespace @@ -115,7 +129,7 @@ public async Task ExecuteAsync(OperationContext operationContext, private WriteCommandOperation CreateOperation(ICoreSessionHandle session, ConnectionDescription connectionDescription) { var command = CreateCommand(session, connectionDescription); - return new WriteCommandOperation(DatabaseNamespace.Admin, command, BsonDocumentSerializer.Instance, _messageEncoderSettings); + return new WriteCommandOperation(DatabaseNamespace.Admin, command, BsonDocumentSerializer.Instance, _messageEncoderSettings, _serializationDomain); } } } diff --git a/src/MongoDB.Driver/Core/Operations/RetryableDeleteCommandOperation.cs b/src/MongoDB.Driver/Core/Operations/RetryableDeleteCommandOperation.cs index 480c152a749..4f13bc72d23 100644 --- a/src/MongoDB.Driver/Core/Operations/RetryableDeleteCommandOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/RetryableDeleteCommandOperation.cs @@ -37,13 +37,23 @@ internal sealed class RetryableDeleteCommandOperation : RetryableWriteCommandOpe public RetryableDeleteCommandOperation( CollectionNamespace collectionNamespace, BatchableSource deletes, - MessageEncoderSettings messageEncoderSettings) - : base(Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)).DatabaseNamespace, messageEncoderSettings) + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) + : base(Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)).DatabaseNamespace, messageEncoderSettings, serializationDomain) { _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); _deletes = Ensure.IsNotNull(deletes, nameof(deletes)); } + //EXIT + public RetryableDeleteCommandOperation( + CollectionNamespace collectionNamespace, + BatchableSource deletes, + MessageEncoderSettings messageEncoderSettings) + : this(collectionNamespace, deletes, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain) + { + } + public BsonDocument Let { get { return _let; } diff --git a/src/MongoDB.Driver/Core/Operations/RetryableInsertCommandOperation.cs b/src/MongoDB.Driver/Core/Operations/RetryableInsertCommandOperation.cs index 1952ad2d019..dabf2c939b9 100644 --- a/src/MongoDB.Driver/Core/Operations/RetryableInsertCommandOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/RetryableInsertCommandOperation.cs @@ -37,14 +37,26 @@ public RetryableInsertCommandOperation( CollectionNamespace collectionNamespace, BatchableSource documents, IBsonSerializer documentSerializer, - MessageEncoderSettings messageEncoderSettings) - : base(Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)).DatabaseNamespace, messageEncoderSettings) + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) + : base(Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)).DatabaseNamespace, messageEncoderSettings, serializationDomain) { _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); _documents = Ensure.IsNotNull(documents, nameof(documents)); _documentSerializer = Ensure.IsNotNull(documentSerializer, nameof(documentSerializer)); } + //EXIT + public RetryableInsertCommandOperation( + CollectionNamespace collectionNamespace, + BatchableSource documents, + IBsonSerializer documentSerializer, + MessageEncoderSettings messageEncoderSettings) + : this(collectionNamespace, documents, documentSerializer, messageEncoderSettings, + BsonSerializer.DefaultSerializationDomain) + { + } + public bool? BypassDocumentValidation { get { return _bypassDocumentValidation; } @@ -139,7 +151,7 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati { if (_cachedSerializer.ValueType != actualType) { - _cachedSerializer = BsonSerializer.LookupSerializer(actualType); + _cachedSerializer = context.SerializationDomain.LookupSerializer(actualType); } serializer = _cachedSerializer; } diff --git a/src/MongoDB.Driver/Core/Operations/RetryableUpdateCommandOperation.cs b/src/MongoDB.Driver/Core/Operations/RetryableUpdateCommandOperation.cs index e51e6170872..96e24cb3275 100644 --- a/src/MongoDB.Driver/Core/Operations/RetryableUpdateCommandOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/RetryableUpdateCommandOperation.cs @@ -38,13 +38,23 @@ internal sealed class RetryableUpdateCommandOperation : RetryableWriteCommandOpe public RetryableUpdateCommandOperation( CollectionNamespace collectionNamespace, BatchableSource updates, - MessageEncoderSettings messageEncoderSettings) - : base(Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)).DatabaseNamespace, messageEncoderSettings) + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) + : base(Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)).DatabaseNamespace, messageEncoderSettings, serializationDomain) { _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); _updates = Ensure.IsNotNull(updates, nameof(updates)); } + //EXIT + public RetryableUpdateCommandOperation( + CollectionNamespace collectionNamespace, + BatchableSource updates, + MessageEncoderSettings messageEncoderSettings) + : this(collectionNamespace, updates, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain) + { + } + public bool? BypassDocumentValidation { get { return _bypassDocumentValidation; } diff --git a/src/MongoDB.Driver/Core/Operations/RetryableWriteCommandOperationBase.cs b/src/MongoDB.Driver/Core/Operations/RetryableWriteCommandOperationBase.cs index bcb0e72b291..7ee303eaca9 100644 --- a/src/MongoDB.Driver/Core/Operations/RetryableWriteCommandOperationBase.cs +++ b/src/MongoDB.Driver/Core/Operations/RetryableWriteCommandOperationBase.cs @@ -19,6 +19,7 @@ using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.IO; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Misc; @@ -36,14 +37,17 @@ internal abstract class RetryableWriteCommandOperationBase : IWriteOperation _serializationDomain; + public WriteConcern WriteConcern { get { return _writeConcern; } @@ -127,12 +133,15 @@ public BsonDocument ExecuteAttempt(OperationContext operationContext, RetryableW args.PostWriteAction, args.ResponseHandling, BsonDocumentSerializer.Instance, - args.MessageEncoderSettings); + args.MessageEncoderSettings, + _serializationDomain); } public Task ExecuteAttemptAsync(OperationContext operationContext, RetryableWriteContext context, int attempt, long? transactionNumber) { var args = GetCommandArgs(context, attempt, transactionNumber); + var serializationDomain = args.MessageEncoderSettings.GetOrDefault( + MessageEncoderSettingsName.SerializationDomain, BsonSerializer.DefaultSerializationDomain); return context.Channel.CommandAsync( operationContext, context.ChannelSource.Session, @@ -145,7 +154,8 @@ public Task ExecuteAttemptAsync(OperationContext operationContext, args.PostWriteAction, args.ResponseHandling, BsonDocumentSerializer.Instance, - args.MessageEncoderSettings); + args.MessageEncoderSettings, + serializationDomain); } protected abstract BsonDocument CreateCommand(ICoreSessionHandle session, int attempt, long? transactionNumber); diff --git a/src/MongoDB.Driver/Core/Operations/UpdateSearchIndexOperation.cs b/src/MongoDB.Driver/Core/Operations/UpdateSearchIndexOperation.cs index 09496dd9d3b..9df572313c6 100644 --- a/src/MongoDB.Driver/Core/Operations/UpdateSearchIndexOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/UpdateSearchIndexOperation.cs @@ -15,6 +15,7 @@ using System.Threading.Tasks; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Events; @@ -30,17 +31,20 @@ internal sealed class UpdateSearchIndexOperation : IWriteOperation private readonly MessageEncoderSettings _messageEncoderSettings; private readonly string _indexName; private readonly BsonDocument _definition; + private readonly IBsonSerializationDomain _serializationDomain; public UpdateSearchIndexOperation( CollectionNamespace collectionNamespace, string indexName, BsonDocument definition, - MessageEncoderSettings messageEncoderSettings) + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) { _collectionNamespace = Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); _indexName = Ensure.IsNotNullOrEmpty(indexName , nameof(indexName)); _definition = Ensure.IsNotNull(definition, nameof(definition)); _messageEncoderSettings = Ensure.IsNotNull(messageEncoderSettings, nameof(messageEncoderSettings)); + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); } public BsonDocument Execute(OperationContext operationContext, IWriteBinding binding) @@ -76,7 +80,7 @@ private WriteCommandOperation CreateOperation() { "definition", _definition } }; - return new (_collectionNamespace.DatabaseNamespace, command, BsonDocumentSerializer.Instance, _messageEncoderSettings); + return new (_collectionNamespace.DatabaseNamespace, command, BsonDocumentSerializer.Instance, _messageEncoderSettings, _serializationDomain); } } } diff --git a/src/MongoDB.Driver/Core/Operations/WriteCommandOperation.cs b/src/MongoDB.Driver/Core/Operations/WriteCommandOperation.cs index 033db2d0eae..87ed016d0c0 100644 --- a/src/MongoDB.Driver/Core/Operations/WriteCommandOperation.cs +++ b/src/MongoDB.Driver/Core/Operations/WriteCommandOperation.cs @@ -27,8 +27,21 @@ internal sealed class WriteCommandOperation : CommandOperationBa { private ReadPreference _readPreference = ReadPreference.Primary; - public WriteCommandOperation(DatabaseNamespace databaseNamespace, BsonDocument command, IBsonSerializer resultSerializer, MessageEncoderSettings messageEncoderSettings) - : base(databaseNamespace, command, resultSerializer, messageEncoderSettings) + public WriteCommandOperation(DatabaseNamespace databaseNamespace, + BsonDocument command, + IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) + : base(databaseNamespace, command, resultSerializer, messageEncoderSettings, serializationDomain) + { + } + + //EXIT + public WriteCommandOperation(DatabaseNamespace databaseNamespace, + BsonDocument command, + IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings) + : this(databaseNamespace, command, resultSerializer, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain) { } diff --git a/src/MongoDB.Driver/Core/Servers/ServerChannel.cs b/src/MongoDB.Driver/Core/Servers/ServerChannel.cs index bf0ce569e87..5e767db5d7d 100644 --- a/src/MongoDB.Driver/Core/Servers/ServerChannel.cs +++ b/src/MongoDB.Driver/Core/Servers/ServerChannel.cs @@ -63,7 +63,8 @@ public TResult Command( Action postWriteAction, CommandResponseHandling responseHandling, IBsonSerializer resultSerializer, - MessageEncoderSettings messageEncoderSettings) + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) { var roundTripTime = TimeSpan.Zero; if (_server is ISelectedServer selectedServer) @@ -84,12 +85,14 @@ public TResult Command( resultSerializer, messageEncoderSettings, _server.ServerApi, - roundTripTime); + roundTripTime, + serializationDomain); return ExecuteProtocol(operationContext, protocol, session); } - public Task CommandAsync( + //EXIT + public TResult Command( OperationContext operationContext, ICoreSession session, ReadPreference readPreference, @@ -102,6 +105,35 @@ public Task CommandAsync( CommandResponseHandling responseHandling, IBsonSerializer resultSerializer, MessageEncoderSettings messageEncoderSettings) + => Command( + operationContext, + session, + readPreference, + databaseNamespace, + command, + commandPayloads, + commandValidator, + additionalOptions, + postWriteAction, + responseHandling, + resultSerializer, + messageEncoderSettings, + BsonSerializer.DefaultSerializationDomain); + + public Task CommandAsync( + OperationContext operationContext, + ICoreSession session, + ReadPreference readPreference, + DatabaseNamespace databaseNamespace, + BsonDocument command, + IEnumerable commandPayloads, + IElementNameValidator commandValidator, + BsonDocument additionalOptions, + Action postWriteAction, + CommandResponseHandling responseHandling, + IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings, + IBsonSerializationDomain serializationDomain) { var roundTripTime = TimeSpan.Zero; if (_server is ISelectedServer selectedServer) @@ -122,11 +154,40 @@ public Task CommandAsync( resultSerializer, messageEncoderSettings, _server.ServerApi, - roundTripTime); + roundTripTime, + serializationDomain); return ExecuteProtocolAsync(operationContext, protocol, session); } + public Task CommandAsync( + OperationContext operationContext, + ICoreSession session, + ReadPreference readPreference, + DatabaseNamespace databaseNamespace, + BsonDocument command, + IEnumerable commandPayloads, + IElementNameValidator commandValidator, + BsonDocument additionalOptions, + Action postWriteAction, + CommandResponseHandling responseHandling, + IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings) + => CommandAsync( + operationContext, + session, + readPreference, + databaseNamespace, + command, + commandPayloads, + commandValidator, + additionalOptions, + postWriteAction, + responseHandling, + resultSerializer, + messageEncoderSettings, + BsonSerializer.DefaultSerializationDomain); + public void Dispose() { if (_state.TryChange(ChannelState.Initial, ChannelState.Disposed)) diff --git a/src/MongoDB.Driver/Core/WireProtocol/CommandUsingCommandMessageWireProtocol.cs b/src/MongoDB.Driver/Core/WireProtocol/CommandUsingCommandMessageWireProtocol.cs index 276610571d1..b02cc41744c 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/CommandUsingCommandMessageWireProtocol.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/CommandUsingCommandMessageWireProtocol.cs @@ -51,6 +51,7 @@ internal sealed class CommandUsingCommandMessageWireProtocol : I private readonly ServerApi _serverApi; private readonly TimeSpan _roundTripTime; private readonly ICoreSession _session; + private readonly IBsonSerializationDomain _serializationDomain; // streamable fields private bool _moreToCome = false; // MoreToCome from the previous response private int _previousRequestId; // RequestId from the previous response @@ -69,7 +70,8 @@ public CommandUsingCommandMessageWireProtocol( MessageEncoderSettings messageEncoderSettings, Action postWriteAction, ServerApi serverApi, - TimeSpan roundTripTime) + TimeSpan roundTripTime, + IBsonSerializationDomain serializationDomain) { if (responseHandling != CommandResponseHandling.Return && responseHandling != CommandResponseHandling.NoResponseExpected && @@ -91,6 +93,7 @@ public CommandUsingCommandMessageWireProtocol( _postWriteAction = postWriteAction; // can be null _serverApi = serverApi; // can be null _roundTripTime = roundTripTime; + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); if (messageEncoderSettings != null) { @@ -531,7 +534,7 @@ private TCommandResult ProcessResponse(ConnectionId connectionId, CommandMessage { using (var reader = new BsonBinaryReader(stream, binaryReaderSettings)) { - var context = BsonDeserializationContext.CreateRoot(reader); + var context = BsonDeserializationContext.CreateRoot(reader, _serializationDomain); return _resultSerializer.Deserialize(context); } } diff --git a/src/MongoDB.Driver/Core/WireProtocol/CommandUsingQueryMessageWireProtocol.cs b/src/MongoDB.Driver/Core/WireProtocol/CommandUsingQueryMessageWireProtocol.cs index ecfb53f0f70..feaa20cee41 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/CommandUsingQueryMessageWireProtocol.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/CommandUsingQueryMessageWireProtocol.cs @@ -49,6 +49,7 @@ internal sealed class CommandUsingQueryMessageWireProtocol : IWi private readonly IBsonSerializer _resultSerializer; private readonly ServerApi _serverApi; private readonly ICoreSession _session; + private readonly IBsonSerializationDomain _serializationDomain; // constructors public CommandUsingQueryMessageWireProtocol( @@ -63,7 +64,8 @@ public CommandUsingQueryMessageWireProtocol( IBsonSerializer resultSerializer, MessageEncoderSettings messageEncoderSettings, Action postWriteAction, - ServerApi serverApi) + ServerApi serverApi, + IBsonSerializationDomain serializationDomain) { if (responseHandling != CommandResponseHandling.Return && responseHandling != CommandResponseHandling.Ignore) { @@ -82,6 +84,7 @@ public CommandUsingQueryMessageWireProtocol( _messageEncoderSettings = messageEncoderSettings; _postWriteAction = postWriteAction; // can be null _serverApi = serverApi; // can be null + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); } // public properties @@ -326,7 +329,7 @@ private TCommandResult ProcessReply(ConnectionId connectionId, ReplyMessage)encoderFactory.GetReplyMessageEncoder(_resultSerializer); using (var reader = encoder.CreateBinaryReader()) { - var context = BsonDeserializationContext.CreateRoot(reader); + var context = BsonDeserializationContext.CreateRoot(reader, _serializationDomain); return _resultSerializer.Deserialize(context); } } diff --git a/src/MongoDB.Driver/Core/WireProtocol/CommandWireProtocol.cs b/src/MongoDB.Driver/Core/WireProtocol/CommandWireProtocol.cs index 3871fdddca8..ec849835326 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/CommandWireProtocol.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/CommandWireProtocol.cs @@ -46,6 +46,7 @@ internal sealed class CommandWireProtocol : IWireProtocol resultSerializer, MessageEncoderSettings messageEncoderSettings, - ServerApi serverApi) + ServerApi serverApi, + IBsonSerializationDomain serializationDomain) : this( databaseNamespace, command, @@ -62,18 +64,39 @@ public CommandWireProtocol( CommandResponseHandling.Return, resultSerializer, messageEncoderSettings, - serverApi) + serverApi, + serializationDomain) { } + //EXIT public CommandWireProtocol( DatabaseNamespace databaseNamespace, BsonDocument command, bool secondaryOk, - CommandResponseHandling commandResponseHandling, IBsonSerializer resultSerializer, MessageEncoderSettings messageEncoderSettings, ServerApi serverApi) + : this( + databaseNamespace, + command, + secondaryOk, + resultSerializer, + messageEncoderSettings, + serverApi, + BsonSerializer.DefaultSerializationDomain) + { + } + + public CommandWireProtocol( + DatabaseNamespace databaseNamespace, + BsonDocument command, + bool secondaryOk, + CommandResponseHandling commandResponseHandling, + IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings, + ServerApi serverApi, + IBsonSerializationDomain serializationDomain) : this( NoCoreSession.Instance, secondaryOk ? ReadPreference.PrimaryPreferred : ReadPreference.Primary, @@ -87,7 +110,29 @@ public CommandWireProtocol( resultSerializer, messageEncoderSettings, serverApi, - roundTripTime: TimeSpan.Zero) + roundTripTime: TimeSpan.Zero, + serializationDomain) + { + } + + //EXIT + public CommandWireProtocol( + DatabaseNamespace databaseNamespace, + BsonDocument command, + bool secondaryOk, + CommandResponseHandling commandResponseHandling, + IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings, + ServerApi serverApi) + : this( + databaseNamespace, + command, + secondaryOk, + commandResponseHandling, + resultSerializer, + messageEncoderSettings, + serverApi, + BsonSerializer.DefaultSerializationDomain) { } @@ -104,7 +149,8 @@ public CommandWireProtocol( IBsonSerializer resultSerializer, MessageEncoderSettings messageEncoderSettings, ServerApi serverApi, - TimeSpan roundTripTime) + TimeSpan roundTripTime, + IBsonSerializationDomain serializationDomain) { if (responseHandling != CommandResponseHandling.Return && responseHandling != CommandResponseHandling.NoResponseExpected && @@ -126,6 +172,40 @@ public CommandWireProtocol( _postWriteAction = postWriteAction; // can be null _serverApi = serverApi; // can be null _roundTripTime = roundTripTime; + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); + } + + //EXIT + public CommandWireProtocol( + ICoreSession session, + ReadPreference readPreference, + DatabaseNamespace databaseNamespace, + BsonDocument command, + IEnumerable commandPayloads, + IElementNameValidator commandValidator, + BsonDocument additionalOptions, + Action postWriteAction, + CommandResponseHandling responseHandling, + IBsonSerializer resultSerializer, + MessageEncoderSettings messageEncoderSettings, + ServerApi serverApi, + TimeSpan roundTripTime) + : this( + session, + readPreference, + databaseNamespace, + command, + commandPayloads, + commandValidator, + additionalOptions, + postWriteAction, + responseHandling, + resultSerializer, + messageEncoderSettings, + serverApi, + roundTripTime, + BsonSerializer.DefaultSerializationDomain) + { } // public properties @@ -160,7 +240,8 @@ private IWireProtocol CreateCommandUsingCommandMessageWireProtoc _messageEncoderSettings, _postWriteAction, _serverApi, - _roundTripTime); + _roundTripTime, + _serializationDomain); } private IWireProtocol CreateCommandUsingQueryMessageWireProtocol() @@ -179,7 +260,8 @@ private IWireProtocol CreateCommandUsingQueryMessageWireProtocol _resultSerializer, _messageEncoderSettings, _postWriteAction, - _serverApi); + _serverApi, + _serializationDomain); } private IWireProtocol CreateSupportedWireProtocol(IConnection connection) diff --git a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/ClientBulkWriteOpsSectionFormatter.cs b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/ClientBulkWriteOpsSectionFormatter.cs index 8fce04fb521..1a8d6d44376 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/ClientBulkWriteOpsSectionFormatter.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/ClientBulkWriteOpsSectionFormatter.cs @@ -31,11 +31,11 @@ internal sealed class ClientBulkWriteOpsSectionFormatter : ICommandMessageSectio private readonly Dictionary _nsInfos; private MemoryStream _nsInfoMemoryStream; private BsonBinaryWriter _nsInfoWriter; - private IBsonSerializerRegistry _serializerRegistry; + private IBsonSerializationDomain _serializationDomain; private Dictionary _idsMap; private int _currentIndex; - public ClientBulkWriteOpsSectionFormatter(long? maxSize) + public ClientBulkWriteOpsSectionFormatter(long? maxSize, IBsonSerializationDomain serializationDomain) { _maxSize = (maxSize ?? long.MaxValue) - 1000; // according to spec we should leave some extra space for further overhead if (_maxSize <= 0) @@ -46,6 +46,7 @@ public ClientBulkWriteOpsSectionFormatter(long? maxSize) _nsInfos = new Dictionary(); _nsInfoMemoryStream = new MemoryStream(); _nsInfoWriter = new BsonBinaryWriter(_nsInfoMemoryStream); + _serializationDomain = serializationDomain; } public void Dispose() @@ -63,8 +64,7 @@ public void FormatSection(ClientBulkWriteOpsCommandMessageSection section, IBson throw new ArgumentException("Writer must be an instance of BsonBinaryWriter."); } - _serializerRegistry = BsonSerializer.SerializerRegistry; - var serializationContext = BsonSerializationContext.CreateRoot(binaryWriter); + var serializationContext = BsonSerializationContext.CreateRoot(binaryWriter, _serializationDomain); _idsMap = section.IdsMap; var stream = binaryWriter.BsonStream; var startPosition = stream.Position; @@ -126,7 +126,7 @@ public void FormatSection(ClientBulkWriteOpsCommandMessageSection section, IBson public void RenderDeleteMany(RenderArgs renderArgs, BsonSerializationContext serializationContext, BulkWriteDeleteManyModel model) { WriteStartModel(serializationContext, "delete", model); - var documentSerializer = _serializerRegistry.GetSerializer(); + var documentSerializer = _serializationDomain.SerializerRegistry.GetSerializer(); WriteFilter(serializationContext, renderArgs, model.Filter, documentSerializer); WriteBoolean(serializationContext, "multi", true); WriteHint(serializationContext, model.Hint); @@ -137,7 +137,7 @@ public void RenderDeleteMany(RenderArgs renderArgs, Bso public void RenderDeleteOne(RenderArgs renderArgs, BsonSerializationContext serializationContext, BulkWriteDeleteOneModel model) { WriteStartModel(serializationContext, "delete", model); - var documentSerializer = _serializerRegistry.GetSerializer(); + var documentSerializer = _serializationDomain.SerializerRegistry.GetSerializer(); WriteFilter(serializationContext, renderArgs, model.Filter, documentSerializer); WriteBoolean(serializationContext, "multi", false); WriteHint(serializationContext, model.Hint); @@ -148,8 +148,8 @@ public void RenderDeleteOne(RenderArgs renderArgs, Bson public void RenderInsertOne(RenderArgs renderArgs, BsonSerializationContext serializationContext, BulkWriteInsertOneModel model) { WriteStartModel(serializationContext, "insert", model); - var documentSerializer = _serializerRegistry.GetSerializer(); - var documentId = documentSerializer.SetDocumentIdIfMissing(null, model.Document); + var documentSerializer = _serializationDomain.SerializerRegistry.GetSerializer(); + var documentId = documentSerializer.SetDocumentIdIfMissing(null, model.Document, serializationContext.SerializationDomain); _idsMap[_currentIndex] = BsonValue.Create(documentId); serializationContext.Writer.WriteName("document"); documentSerializer.Serialize(serializationContext, model.Document); @@ -159,7 +159,7 @@ public void RenderInsertOne(RenderArgs renderArgs, Bson public void RenderReplaceOne(RenderArgs renderArgs, BsonSerializationContext serializationContext, BulkWriteReplaceOneModel model) { WriteStartModel(serializationContext, "update", model); - var documentSerializer = _serializerRegistry.GetSerializer(); + var documentSerializer = _serializationDomain.SerializerRegistry.GetSerializer(); WriteFilter(serializationContext, renderArgs, model.Filter, documentSerializer); WriteUpdate(serializationContext, model.Replacement, documentSerializer, UpdateType.Replacement); if (model.IsUpsert) @@ -177,7 +177,7 @@ public void RenderReplaceOne(RenderArgs renderArgs, Bso public void RenderUpdateMany(RenderArgs renderArgs, BsonSerializationContext serializationContext, BulkWriteUpdateManyModel model) { WriteStartModel(serializationContext, "update", model); - var documentSerializer = _serializerRegistry.GetSerializer(); + var documentSerializer = _serializationDomain.SerializerRegistry.GetSerializer(); WriteFilter(serializationContext, renderArgs, model.Filter, documentSerializer); WriteUpdate(serializationContext, renderArgs, model.Update, documentSerializer); if (model.IsUpsert) @@ -195,7 +195,7 @@ public void RenderUpdateMany(RenderArgs renderArgs, Bso public void RenderUpdateOne(RenderArgs renderArgs, BsonSerializationContext serializationContext, BulkWriteUpdateOneModel model) { WriteStartModel(serializationContext, "update", model); - var documentSerializer = _serializerRegistry.GetSerializer(); + var documentSerializer = _serializationDomain.SerializerRegistry.GetSerializer(); WriteFilter(serializationContext, renderArgs, model.Filter, documentSerializer); WriteUpdate(serializationContext, renderArgs, model.Update, documentSerializer); if (model.IsUpsert) @@ -238,7 +238,7 @@ private void WriteArrayFilters(BsonSerializationContext serializationContext, IE serializationContext.Writer.WriteStartArray(); foreach (var arrayFilter in arrayFilters) { - var renderedArrayFilter = arrayFilter.Render(null, _serializerRegistry); + var renderedArrayFilter = arrayFilter.Render(null, _serializationDomain.SerializerRegistry); BsonDocumentSerializer.Instance.Serialize(serializationContext, renderedArrayFilter); } serializationContext.Writer.WriteEndArray(); diff --git a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/CommandMessageBinaryEncoder.cs b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/CommandMessageBinaryEncoder.cs index 85b4feb8a67..8ae86a8c837 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/CommandMessageBinaryEncoder.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/CommandMessageBinaryEncoder.cs @@ -28,7 +28,8 @@ namespace MongoDB.Driver.Core.WireProtocol.Messages.Encoders.BinaryEncoders internal sealed class CommandMessageBinaryEncoder : MessageBinaryEncoderBase, IMessageEncoder { private const int EncryptedMaxBatchSize = 2 * 1024 * 1024; // 2 MiB - private static readonly ICommandMessageSectionFormatter __type0SectionFormatter = new Type0SectionFormatter(); + private static readonly ICommandMessageSectionFormatter __type0SectionFormatter = new Type0SectionFormatter(BsonSerializer.DefaultSerializationDomain); + //QUESTION Looking at the spec and our implementation, it seems that type 0 sections always serialize/deserialize RawBsonDocument, so they should use the default domain. Am I missing something? // constructors public CommandMessageBinaryEncoder(Stream stream, MessageEncoderSettings encoderSettings) @@ -214,7 +215,7 @@ private List ReadSections(BsonBinaryReader reader, long m private Type0CommandMessageSection ReadType0Section(IBsonReader reader) { var serializer = RawBsonDocumentSerializer.Instance; - var context = BsonDeserializationContext.CreateRoot(reader); + var context = BsonDeserializationContext.CreateRoot(reader, SerializationDomain); var document = serializer.Deserialize(context); return new Type0CommandMessageSection(document, serializer); } @@ -229,7 +230,7 @@ private Type1CommandMessageSection ReadType1Section(BsonBinaryR var payloadEndPosition = payloadStartPosition + payloadLength; var identifier = stream.ReadCString(Utf8Encodings.Strict); var serializer = RawBsonDocumentSerializer.Instance; - var context = BsonDeserializationContext.CreateRoot(reader); + var context = BsonDeserializationContext.CreateRoot(reader, SerializationDomain); var documents = new List(); while (stream.Position < payloadEndPosition) { @@ -252,11 +253,11 @@ private void WriteSection(BsonBinaryWriter writer, CommandMessageSection section __type0SectionFormatter.FormatSection(type0Section, writer); break; case Type1CommandMessageSection type1Section: - var type1SectionFormatter = new Type1SectionFormatter(GetSectionMaxSize()); + var type1SectionFormatter = new Type1SectionFormatter(GetSectionMaxSize(), SerializationDomain); type1SectionFormatter.FormatSection(type1Section, writer); break; case ClientBulkWriteOpsCommandMessageSection bulkWriteOpsSection: - using (var bulkWriteOpsSectionFormatter = new ClientBulkWriteOpsSectionFormatter(GetSectionMaxSize())) + using (var bulkWriteOpsSectionFormatter = new ClientBulkWriteOpsSectionFormatter(GetSectionMaxSize(), SerializationDomain)) { bulkWriteOpsSectionFormatter.FormatSection(bulkWriteOpsSection, writer); } diff --git a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/MessageBinaryEncoderBase.cs b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/MessageBinaryEncoderBase.cs index e8e6e58b61d..71c07ee442b 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/MessageBinaryEncoderBase.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/MessageBinaryEncoderBase.cs @@ -15,8 +15,8 @@ using System.IO; using System.Text; -using MongoDB.Bson; using MongoDB.Bson.IO; +using MongoDB.Bson.Serialization; using MongoDB.Driver.Core.Misc; namespace MongoDB.Driver.Core.WireProtocol.Messages.Encoders.BinaryEncoders @@ -82,6 +82,10 @@ protected int? MaxWireDocumentSize } } + protected IBsonSerializationDomain SerializationDomain + => _encoderSettings?.GetOrDefault(MessageEncoderSettingsName.SerializationDomain, null) ?? BsonSerializer.DefaultSerializationDomain; + //QUESTION Is this correct? If we don't have a domain in the encoder settings, just use the default one? + // methods public BsonBinaryReader CreateBinaryReader() { diff --git a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/QueryMessageBinaryEncoder.cs b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/QueryMessageBinaryEncoder.cs index 45588a3e0b6..7146d4eec7b 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/QueryMessageBinaryEncoder.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/QueryMessageBinaryEncoder.cs @@ -85,7 +85,7 @@ internal QueryMessage ReadMessage(IBsonSerializer serializ var fullCollectionName = stream.ReadCString(Encoding); var skip = stream.ReadInt32(); var batchSize = stream.ReadInt32(); - var context = BsonDeserializationContext.CreateRoot(binaryReader); + var context = BsonDeserializationContext.CreateRoot(binaryReader, SerializationDomain); var query = serializer.Deserialize(context); BsonDocument fields = null; if (stream.Position < startPosition + messageSize) @@ -156,7 +156,8 @@ private void WriteOptionalFields(BsonBinaryWriter binaryWriter, BsonDocument fie { if (fields != null) { - var context = BsonSerializationContext.CreateRoot(binaryWriter); + //QUESTION Is it correct we only need a default domain here? + var context = BsonSerializationContext.CreateRoot(binaryWriter, BsonSerializer.DefaultSerializationDomain); BsonDocumentSerializer.Instance.Serialize(context, fields); } } @@ -169,7 +170,8 @@ private void WriteQuery(BsonBinaryWriter binaryWriter, BsonDocument query, IElem binaryWriter.PushElementNameValidator(queryValidator); try { - var context = BsonSerializationContext.CreateRoot(binaryWriter); + //QUESTION Is it correct we only need a default domain here? + var context = BsonSerializationContext.CreateRoot(binaryWriter, BsonSerializer.DefaultSerializationDomain); BsonDocumentSerializer.Instance.Serialize(context, query ?? new BsonDocument()); } finally diff --git a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/ReplyMessageBinaryEncoder.cs b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/ReplyMessageBinaryEncoder.cs index d785716b2df..51594e6f2ff 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/ReplyMessageBinaryEncoder.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/ReplyMessageBinaryEncoder.cs @@ -60,7 +60,7 @@ public ReplyMessage ReadMessage() if (queryFailure) { - var context = BsonDeserializationContext.CreateRoot(binaryReader); + var context = BsonDeserializationContext.CreateRoot(binaryReader, SerializationDomain); queryFailureDocument = BsonDocumentSerializer.Instance.Deserialize(context); } else @@ -69,7 +69,7 @@ public ReplyMessage ReadMessage() for (var i = 0; i < numberReturned; i++) { var allowDuplicateElementNames = typeof(TDocument) == typeof(BsonDocument); - var context = BsonDeserializationContext.CreateRoot(binaryReader, builder => + var context = BsonDeserializationContext.CreateRoot(binaryReader, SerializationDomain, builder => { builder.AllowDuplicateElementNames = allowDuplicateElementNames; }); @@ -124,14 +124,16 @@ public void WriteMessage(ReplyMessage message) stream.WriteInt32(message.NumberReturned); if (message.QueryFailure) { - var context = BsonSerializationContext.CreateRoot(binaryWriter); + //QUESTION Is it correct we only need a default domain here? + var context = BsonSerializationContext.CreateRoot(binaryWriter, BsonSerializer.DefaultSerializationDomain); _serializer.Serialize(context, message.QueryFailureDocument); } else { foreach (var doc in message.Documents) { - var context = BsonSerializationContext.CreateRoot(binaryWriter); + //QUESTION Is it correct we only need a default domain here? + var context = BsonSerializationContext.CreateRoot(binaryWriter, BsonSerializer.DefaultSerializationDomain); _serializer.Serialize(context, doc); } } diff --git a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/Type0SectionFormatter.cs b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/Type0SectionFormatter.cs index eccb8a7d01b..5444cc2150a 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/Type0SectionFormatter.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/Type0SectionFormatter.cs @@ -20,10 +20,17 @@ namespace MongoDB.Driver.Core.WireProtocol.Messages.Encoders.BinaryEncoders { internal sealed class Type0SectionFormatter : ICommandMessageSectionFormatter { + private readonly IBsonSerializationDomain _serializationDomain; + + public Type0SectionFormatter(IBsonSerializationDomain serializationDomain) + { + _serializationDomain = serializationDomain; + } + public void FormatSection(Type0CommandMessageSection section, IBsonWriter writer) { var serializer = section.DocumentSerializer; - var context = BsonSerializationContext.CreateRoot(writer); + var context = BsonSerializationContext.CreateRoot(writer, _serializationDomain); serializer.Serialize(context, section.Document); } } diff --git a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/Type1SectionFormatter.cs b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/Type1SectionFormatter.cs index f56b9e1bb46..49784613043 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/Type1SectionFormatter.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/BinaryEncoders/Type1SectionFormatter.cs @@ -22,10 +22,12 @@ namespace MongoDB.Driver.Core.WireProtocol.Messages.Encoders.BinaryEncoders internal sealed class Type1SectionFormatter : ICommandMessageSectionFormatter { private readonly long? _maxSize; + private readonly IBsonSerializationDomain _serializationDomain; - public Type1SectionFormatter(long? maxSize) + public Type1SectionFormatter(long? maxSize, IBsonSerializationDomain serializationDomain) { _maxSize = maxSize; + _serializationDomain = serializationDomain; } public void FormatSection(Type1CommandMessageSection section, IBsonWriter writer) @@ -37,7 +39,7 @@ public void FormatSection(Type1CommandMessageSection section, IBsonWriter writer var stream = binaryWriter.BsonStream; var serializer = section.DocumentSerializer; - var context = BsonSerializationContext.CreateRoot(binaryWriter); + var context = BsonSerializationContext.CreateRoot(binaryWriter, _serializationDomain); var startPosition = stream.Position; stream.WriteInt32(0); // size diff --git a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/ICommandMessageSectionFormatter.cs b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/ICommandMessageSectionFormatter.cs index 3a80661a634..e79dab13765 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/ICommandMessageSectionFormatter.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/ICommandMessageSectionFormatter.cs @@ -14,6 +14,7 @@ */ using MongoDB.Bson.IO; +using MongoDB.Bson.Serialization; namespace MongoDB.Driver.Core.WireProtocol.Messages.Encoders { diff --git a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/CommandMessageJsonEncoder.cs b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/CommandMessageJsonEncoder.cs index 8bbce7ac8d2..ae5b1bdef97 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/CommandMessageJsonEncoder.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/CommandMessageJsonEncoder.cs @@ -27,19 +27,22 @@ namespace MongoDB.Driver.Core.WireProtocol.Messages.Encoders.JsonEncoders { internal sealed class CommandMessageJsonEncoder : MessageJsonEncoderBase, IMessageEncoder { - private static readonly ICommandMessageSectionFormatter __type0SectionFormatter = new Type0SectionFormatter(); - private static readonly ICommandMessageSectionFormatter __type1SectionFormatter = new Type1SectionFormatter(); + private readonly ICommandMessageSectionFormatter _type0SectionFormatter; + private readonly ICommandMessageSectionFormatter _type1SectionFormatter; public CommandMessageJsonEncoder(TextReader textReader, TextWriter textWriter, MessageEncoderSettings encoderSettings) : base(textReader, textWriter, encoderSettings) { + //QUESTION Looking at the spec and our implementation, it seems that type 0 sections always serialize/deserialize RawBsonDocument, so they should use the default domain. Am I missing something? + _type0SectionFormatter = new Type0SectionFormatter(BsonSerializer.DefaultSerializationDomain); + _type1SectionFormatter = new Type1SectionFormatter(SerializationDomain); } // public methods public CommandMessage ReadMessage() { var reader = CreateJsonReader(); - var context = BsonDeserializationContext.CreateRoot(reader); + var context = BsonDeserializationContext.CreateRoot(reader, SerializationDomain); var messageDocument = BsonDocumentSerializer.Instance.Deserialize(context); var opcode = messageDocument["opcode"].AsString; @@ -143,11 +146,11 @@ private void WriteSection(IBsonWriter writer, CommandMessageSection section) switch (section) { case Type0CommandMessageSection type0Section: - __type0SectionFormatter.FormatSection(type0Section, writer); + _type0SectionFormatter.FormatSection(type0Section, writer); break; case Type1CommandMessageSection type1Section: - __type1SectionFormatter.FormatSection(type1Section, writer); + _type1SectionFormatter.FormatSection(type1Section, writer); break; default: diff --git a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/CompressedMessageJsonEncoder.cs b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/CompressedMessageJsonEncoder.cs index a7093627b1c..d6af06a2e97 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/CompressedMessageJsonEncoder.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/CompressedMessageJsonEncoder.cs @@ -41,7 +41,7 @@ public CompressedMessageJsonEncoder(TextReader textReader, TextWriter textWriter public CompressedMessage ReadMessage() { var reader = CreateJsonReader(); - var context = BsonDeserializationContext.CreateRoot(reader); + var context = BsonDeserializationContext.CreateRoot(reader, SerializationDomain); var messageDocument = BsonDocumentSerializer.Instance.Deserialize(context); var opcode = (Opcode)messageDocument["opcode"].ToInt32(); diff --git a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/MessageJsonEncoderBase.cs b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/MessageJsonEncoderBase.cs index 0a54dc96a30..0e2a5b67982 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/MessageJsonEncoderBase.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/MessageJsonEncoderBase.cs @@ -17,6 +17,7 @@ using System.IO; using MongoDB.Bson; using MongoDB.Bson.IO; +using MongoDB.Bson.Serialization; using MongoDB.Driver.Core.Misc; namespace MongoDB.Driver.Core.WireProtocol.Messages.Encoders.JsonEncoders @@ -37,6 +38,8 @@ protected MessageJsonEncoderBase(TextReader textReader, TextWriter textWriter, M _encoderSettings = encoderSettings; } + protected IBsonSerializationDomain SerializationDomain => _encoderSettings?.GetOrDefault(MessageEncoderSettingsName.SerializationDomain, null) ?? BsonSerializer.DefaultSerializationDomain; + // methods public JsonReader CreateJsonReader() { diff --git a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/QueryMessageJsonEncoder.cs b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/QueryMessageJsonEncoder.cs index 83259686e1b..80fc3eaba56 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/QueryMessageJsonEncoder.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/QueryMessageJsonEncoder.cs @@ -25,17 +25,21 @@ namespace MongoDB.Driver.Core.WireProtocol.Messages.Encoders.JsonEncoders { internal sealed class QueryMessageJsonEncoder : MessageJsonEncoderBase, IMessageEncoder { + private IBsonSerializationDomain _serializationDomain; + // constructors public QueryMessageJsonEncoder(TextReader textReader, TextWriter textWriter, MessageEncoderSettings encoderSettings) : base(textReader, textWriter, encoderSettings) { + _serializationDomain = encoderSettings?.GetOrDefault(MessageEncoderSettingsName.SerializationDomain, null) ?? BsonSerializer.DefaultSerializationDomain; + //QUESTION: Should we use the default serialization domain here? I think it's appropriate. } // methods public QueryMessage ReadMessage() { var jsonReader = CreateJsonReader(); - var messageContext = BsonDeserializationContext.CreateRoot(jsonReader); + var messageContext = BsonDeserializationContext.CreateRoot(jsonReader, _serializationDomain); var messageDocument = BsonDocumentSerializer.Instance.Deserialize(messageContext); var opcode = messageDocument["opcode"].AsString; @@ -101,7 +105,7 @@ public void WriteMessage(QueryMessage message) }; var jsonWriter = CreateJsonWriter(); - var messageContext = BsonSerializationContext.CreateRoot(jsonWriter); + var messageContext = BsonSerializationContext.CreateRoot(jsonWriter, _serializationDomain); BsonDocumentSerializer.Instance.Serialize(messageContext, messageDocument); } diff --git a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/ReplyMessageJsonEncoder.cs b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/ReplyMessageJsonEncoder.cs index 3051819f2b6..87bed75b0bb 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/ReplyMessageJsonEncoder.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/ReplyMessageJsonEncoder.cs @@ -29,19 +29,21 @@ internal sealed class ReplyMessageJsonEncoder : MessageJsonEncoderBas { // fields private readonly IBsonSerializer _serializer; + private readonly IBsonSerializationDomain _serializationDomain; // constructors public ReplyMessageJsonEncoder(TextReader textReader, TextWriter textWriter, MessageEncoderSettings encoderSettings, IBsonSerializer serializer) : base(textReader, textWriter, encoderSettings) { _serializer = Ensure.IsNotNull(serializer, nameof(serializer)); + _serializationDomain = encoderSettings?.GetOrDefault(MessageEncoderSettingsName.SerializationDomain, null) ?? BsonSerializer.DefaultSerializationDomain; } // methods public ReplyMessage ReadMessage() { var jsonReader = CreateJsonReader(); - var messageContext = BsonDeserializationContext.CreateRoot(jsonReader); + var messageContext = BsonDeserializationContext.CreateRoot(jsonReader, _serializationDomain); var messageDocument = BsonDocumentSerializer.Instance.Deserialize(messageContext); var opcode = messageDocument["opcode"].AsString; @@ -67,7 +69,7 @@ public ReplyMessage ReadMessage() { using (var documentReader = new BsonDocumentReader(serializedDocument)) { - var documentContext = BsonDeserializationContext.CreateRoot(documentReader); + var documentContext = BsonDeserializationContext.CreateRoot(documentReader, _serializationDomain); var document = _serializer.Deserialize(documentContext); documents.Add(document); } @@ -121,7 +123,7 @@ public void WriteMessage(ReplyMessage message) }; var jsonWriter = CreateJsonWriter(); - var messageContext = BsonSerializationContext.CreateRoot(jsonWriter); + var messageContext = BsonSerializationContext.CreateRoot(jsonWriter, _serializationDomain); BsonDocumentSerializer.Instance.Serialize(messageContext, messageDocument); } diff --git a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/Type0SectionFormatter.cs b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/Type0SectionFormatter.cs index 389184f63bc..c0a213a94f6 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/Type0SectionFormatter.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/Type0SectionFormatter.cs @@ -20,11 +20,17 @@ namespace MongoDB.Driver.Core.WireProtocol.Messages.Encoders.JsonEncoders { internal sealed class Type0SectionFormatter : ICommandMessageSectionFormatter { + private readonly IBsonSerializationDomain _serializationDomain; + + public Type0SectionFormatter(IBsonSerializationDomain serializationDomain) + { + _serializationDomain = serializationDomain; + } public void FormatSection(Type0CommandMessageSection section, IBsonWriter writer) { writer.WriteName("document"); var serializer = section.DocumentSerializer; - var context = BsonSerializationContext.CreateRoot(writer); + var context = BsonSerializationContext.CreateRoot(writer, _serializationDomain); serializer.Serialize(context, section.Document); } } diff --git a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/Type1SectionFormatter.cs b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/Type1SectionFormatter.cs index 906d17e8029..ea621b65dcd 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/Type1SectionFormatter.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/JsonEncoders/Type1SectionFormatter.cs @@ -20,6 +20,13 @@ namespace MongoDB.Driver.Core.WireProtocol.Messages.Encoders.JsonEncoders { internal sealed class Type1SectionFormatter : ICommandMessageSectionFormatter { + private readonly IBsonSerializationDomain _serializationDomain; + + public Type1SectionFormatter(IBsonSerializationDomain serializationDomain) + { + _serializationDomain = serializationDomain; + } + public void FormatSection(Type1CommandMessageSection section, IBsonWriter writer) { writer.WriteString("identifier", section.Identifier); @@ -27,7 +34,7 @@ public void FormatSection(Type1CommandMessageSection section, IBsonWriter writer writer.WriteStartArray(); var batch = section.Documents; var serializer = section.DocumentSerializer; - var context = BsonSerializationContext.CreateRoot(writer); + var context = BsonSerializationContext.CreateRoot(writer, _serializationDomain); for (var i = 0; i < batch.Count; i++) { var document = batch.Items[batch.Offset + i]; diff --git a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/MessageEncoderSettings.cs b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/MessageEncoderSettings.cs index 30c2e33e2fd..112d77816ca 100644 --- a/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/MessageEncoderSettings.cs +++ b/src/MongoDB.Driver/Core/WireProtocol/Messages/Encoders/MessageEncoderSettings.cs @@ -41,6 +41,7 @@ internal static class MessageEncoderSettingsName public const string ShellVersion = nameof(ShellVersion); // other encoders (if any) might use additional settings + public const string SerializationDomain = nameof(SerializationDomain); //FP I think I should try to remove it from here.. } internal sealed class MessageEncoderSettings : IEnumerable> diff --git a/src/MongoDB.Driver/CreateCollectionOptions.cs b/src/MongoDB.Driver/CreateCollectionOptions.cs index e5e62381158..56fff2073b5 100644 --- a/src/MongoDB.Driver/CreateCollectionOptions.cs +++ b/src/MongoDB.Driver/CreateCollectionOptions.cs @@ -38,6 +38,7 @@ public class CreateCollectionOptions private TimeSeriesOptions _timeSeriesOptions; private bool? _usePowerOf2Sizes; private IBsonSerializerRegistry _serializerRegistry; + private IBsonSerializationDomain _serializationDomain; private DocumentValidationAction? _validationAction; private DocumentValidationLevel? _validationLevel; @@ -127,6 +128,8 @@ public bool? NoPadding set { _noPadding = value; } } + //DOMAIN-API We need to remove this, and have only the SerializationDomain property. When we have builder, we will add Obsolete + //We should also decide if we even need any of those two properties. /// /// Gets or sets the serializer registry. /// @@ -136,6 +139,12 @@ public IBsonSerializerRegistry SerializerRegistry set { _serializerRegistry = value; } } + internal IBsonSerializationDomain SerializationDomain + { + get => _serializationDomain; + set => _serializationDomain = value; + } + /// /// Gets or sets the storage engine options. /// @@ -204,6 +213,7 @@ public virtual CreateCollectionOptions Clone() => _maxDocuments = _maxDocuments, _maxSize = _maxSize, _serializerRegistry = _serializerRegistry, + _serializationDomain = _serializationDomain, _storageEngine = _storageEngine, _timeSeriesOptions = _timeSeriesOptions, _validationAction = _validationAction, @@ -244,6 +254,7 @@ internal static CreateCollectionOptions CoercedFrom(CreateCollectionO MaxDocuments = options.MaxDocuments, MaxSize = options.MaxSize, SerializerRegistry = options.SerializerRegistry, + SerializationDomain = options.SerializationDomain, StorageEngine = options.StorageEngine, TimeSeriesOptions = options.TimeSeriesOptions, ValidationAction = options.ValidationAction, diff --git a/src/MongoDB.Driver/CreateViewOptions.cs b/src/MongoDB.Driver/CreateViewOptions.cs index 1226e714841..0ec4e4ffd3e 100644 --- a/src/MongoDB.Driver/CreateViewOptions.cs +++ b/src/MongoDB.Driver/CreateViewOptions.cs @@ -27,6 +27,7 @@ public class CreateViewOptions private Collation _collation; private IBsonSerializer _documentSerializer; private IBsonSerializerRegistry _serializerRegistry; + private IBsonSerializationDomain _serializationDomain; // properties /// @@ -53,6 +54,8 @@ public IBsonSerializer DocumentSerializer set { _documentSerializer = value; } } + //DOMAIN-API We need to remove this, and have only the SerializationDomain property. + //We should also decide if we even need any of those two properties. /// /// Gets or sets the serializer registry. /// @@ -64,5 +67,11 @@ public IBsonSerializerRegistry SerializerRegistry get { return _serializerRegistry; } set { _serializerRegistry = value; } } + + internal IBsonSerializationDomain SerializationDomain + { + get => _serializationDomain; + set => _serializationDomain = value; + } } } diff --git a/src/MongoDB.Driver/FieldDefinition.cs b/src/MongoDB.Driver/FieldDefinition.cs index 6a3f1319da6..9b850457aff 100644 --- a/src/MongoDB.Driver/FieldDefinition.cs +++ b/src/MongoDB.Driver/FieldDefinition.cs @@ -242,7 +242,7 @@ public LambdaExpression Expression /// public override RenderedFieldDefinition Render(RenderArgs args) { - return LinqProviderAdapter.TranslateExpressionToField(_expression, args.DocumentSerializer, args.SerializerRegistry, args.TranslationOptions); + return LinqProviderAdapter.TranslateExpressionToField(_expression, args.DocumentSerializer, args.SerializationDomain, args.TranslationOptions); } } @@ -275,7 +275,7 @@ public Expression> Expression /// public override RenderedFieldDefinition Render(RenderArgs args) { - return LinqProviderAdapter.TranslateExpressionToField(_expression, args.DocumentSerializer, args.SerializerRegistry, args.TranslationOptions, args.PathRenderArgs.AllowScalarValueForArray); + return LinqProviderAdapter.TranslateExpressionToField(_expression, args.DocumentSerializer, args.SerializationDomain, args.TranslationOptions, args.PathRenderArgs.AllowScalarValueForArray); } } diff --git a/src/MongoDB.Driver/FieldValueSerializerHelper.cs b/src/MongoDB.Driver/FieldValueSerializerHelper.cs index 68880f7fe18..26110109b03 100644 --- a/src/MongoDB.Driver/FieldValueSerializerHelper.cs +++ b/src/MongoDB.Driver/FieldValueSerializerHelper.cs @@ -20,6 +20,7 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; +using MongoDB.Driver.Linq.Linq3Implementation.Serializers; using MongoDB.Driver.Support; namespace MongoDB.Driver @@ -51,7 +52,7 @@ public static IBsonSerializer GetSerializerForValueType(IBsonSerializer fieldSer // serialize numeric values without converting them if (fieldType.IsNumeric() && valueType.IsNumeric()) { - var valueSerializer = BsonSerializer.SerializerRegistry.GetSerializer(valueType); + var valueSerializer = StandardSerializers.GetSerializer(valueType); if (HasStringRepresentation(fieldSerializer)) { valueSerializer = WithStringRepresentation(valueSerializer); diff --git a/src/MongoDB.Driver/FilterDefinition.cs b/src/MongoDB.Driver/FilterDefinition.cs index a7e727110af..12e03969c72 100644 --- a/src/MongoDB.Driver/FilterDefinition.cs +++ b/src/MongoDB.Driver/FilterDefinition.cs @@ -205,11 +205,11 @@ public override BsonDocument Render(RenderArgs args) { if (args.RenderForElemMatch) { - return LinqProviderAdapter.TranslateExpressionToElemMatchFilter(_expression, elementSerializer: args.DocumentSerializer, args.SerializerRegistry, args.TranslationOptions); + return LinqProviderAdapter.TranslateExpressionToElemMatchFilter(_expression, elementSerializer: args.DocumentSerializer, args.SerializationDomain, args.TranslationOptions); } else { - return LinqProviderAdapter.TranslateExpressionToFilter(_expression, args.DocumentSerializer, args.SerializerRegistry, args.TranslationOptions); + return LinqProviderAdapter.TranslateExpressionToFilter(_expression, args.DocumentSerializer, args.SerializationDomain, args.TranslationOptions); } } } diff --git a/src/MongoDB.Driver/FilterDefinitionBuilder.cs b/src/MongoDB.Driver/FilterDefinitionBuilder.cs index e07d9ac957c..8a1a2416ab0 100644 --- a/src/MongoDB.Driver/FilterDefinitionBuilder.cs +++ b/src/MongoDB.Driver/FilterDefinitionBuilder.cs @@ -1853,7 +1853,7 @@ public override BsonDocument Render(RenderArgs args) var document = new BsonDocument(); using (var bsonWriter = new BsonDocumentWriter(document)) { - var context = BsonSerializationContext.CreateRoot(bsonWriter); + var context = BsonSerializationContext.CreateRoot(bsonWriter, args.SerializationDomain); bsonWriter.WriteStartDocument(); bsonWriter.WriteName(renderedField.FieldName); bsonWriter.WriteStartDocument(); @@ -2013,7 +2013,7 @@ public override BsonDocument Render(RenderArgs args) var document = new BsonDocument(); using (var bsonWriter = new BsonDocumentWriter(document)) { - var context = BsonSerializationContext.CreateRoot(bsonWriter); + var context = BsonSerializationContext.CreateRoot(bsonWriter, args.SerializationDomain); bsonWriter.WriteStartDocument(); bsonWriter.WriteName(renderedField.FieldName); bsonWriter.WriteStartDocument(); @@ -2055,7 +2055,7 @@ public override BsonDocument Render(RenderArgs args) var document = new BsonDocument(); using (var bsonWriter = new BsonDocumentWriter(document)) { - var context = BsonSerializationContext.CreateRoot(bsonWriter); + var context = BsonSerializationContext.CreateRoot(bsonWriter, args.SerializationDomain); bsonWriter.WriteStartDocument(); bsonWriter.WriteName(renderedField.FieldName); bsonWriter.WriteStartDocument(); @@ -2205,14 +2205,14 @@ public override BsonDocument Render(RenderArgs args) } else { - var discriminatorConvention = args.DocumentSerializer.GetDiscriminatorConvention(); + var discriminatorConvention = args.DocumentSerializer.GetDiscriminatorConvention(args.SerializationDomain); if (discriminatorConvention == null) { var message = string.Format("OfType requires a discriminator convention for type: {0}.", BsonUtils.GetFriendlyTypeName(typeof(TDocument))); throw new NotSupportedException(message); } - var discriminator = discriminatorConvention.GetDiscriminator(typeof(TDocument), typeof(TDerived)); + var discriminator = discriminatorConvention.GetDiscriminatorInternal(typeof(TDocument), typeof(TDerived), args.SerializationDomain); if (discriminator == null) { throw new NotSupportedException($"OfType requires that documents of type {BsonUtils.GetFriendlyTypeName(typeof(TDerived))} have a discriminator value."); @@ -2221,8 +2221,8 @@ public override BsonDocument Render(RenderArgs args) var discriminatorField = new AstFilterField(discriminatorConvention.ElementName); ofTypeFilter= discriminatorConvention switch { - IHierarchicalDiscriminatorConvention hierarchicalDiscriminatorConvention => DiscriminatorAstFilter.TypeIs(discriminatorField, hierarchicalDiscriminatorConvention, nominalType, actualType), - IScalarDiscriminatorConvention scalarDiscriminatorConvention => DiscriminatorAstFilter.TypeIs(discriminatorField, scalarDiscriminatorConvention, nominalType, actualType), + IHierarchicalDiscriminatorConvention hierarchicalDiscriminatorConvention => DiscriminatorAstFilter.TypeIs(discriminatorField, hierarchicalDiscriminatorConvention, nominalType, actualType, args.SerializationDomain), + IScalarDiscriminatorConvention scalarDiscriminatorConvention => DiscriminatorAstFilter.TypeIs(discriminatorField, scalarDiscriminatorConvention, nominalType, actualType, args.SerializationDomain), _ => throw new NotSupportedException("OfType is not supported with the configured discriminator convention.") }; } @@ -2268,7 +2268,7 @@ public override BsonDocument Render(RenderArgs args) } else { - var discriminatorConvention = renderedField.FieldSerializer.GetDiscriminatorConvention(); + var discriminatorConvention = renderedField.FieldSerializer.GetDiscriminatorConvention(args.SerializationDomain); if (discriminatorConvention == null) { var message = string.Format("OfType requires a discriminator convention for type: {0}.", BsonUtils.GetFriendlyTypeName(typeof(TField))); @@ -2280,8 +2280,8 @@ public override BsonDocument Render(RenderArgs args) ofTypeFilter = discriminatorConvention switch { - IHierarchicalDiscriminatorConvention hierarchicalDiscriminatorConvention => DiscriminatorAstFilter.TypeIs(discriminatorField, hierarchicalDiscriminatorConvention, nominalType, actualType), - IScalarDiscriminatorConvention scalarDiscriminatorConvention => DiscriminatorAstFilter.TypeIs(discriminatorField, scalarDiscriminatorConvention, nominalType, actualType), + IHierarchicalDiscriminatorConvention hierarchicalDiscriminatorConvention => DiscriminatorAstFilter.TypeIs(discriminatorField, hierarchicalDiscriminatorConvention, nominalType, actualType, args.SerializationDomain), + IScalarDiscriminatorConvention scalarDiscriminatorConvention => DiscriminatorAstFilter.TypeIs(discriminatorField, scalarDiscriminatorConvention, nominalType, actualType, args.SerializationDomain), _ => throw new NotSupportedException("OfType is not supported with the configured discriminator convention.") }; } @@ -2351,7 +2351,7 @@ public override BsonDocument Render(RenderArgs args) var document = new BsonDocument(); using (var bsonWriter = new BsonDocumentWriter(document)) { - var context = BsonSerializationContext.CreateRoot(bsonWriter); + var context = BsonSerializationContext.CreateRoot(bsonWriter, args.SerializationDomain); bsonWriter.WriteStartDocument(); bsonWriter.WriteName(renderedField.FieldName); bsonWriter.WriteStartDocument(); @@ -2445,7 +2445,7 @@ public override BsonDocument Render(RenderArgs args) var document = new BsonDocument(); using (var bsonWriter = new BsonDocumentWriter(document)) { - var context = BsonSerializationContext.CreateRoot(bsonWriter); + var context = BsonSerializationContext.CreateRoot(bsonWriter, args.SerializationDomain); bsonWriter.WriteStartDocument(); bsonWriter.WriteName(renderedField.FieldName); @@ -2495,7 +2495,7 @@ public override BsonDocument Render(RenderArgs args) var document = new BsonDocument(); using (var bsonWriter = new BsonDocumentWriter(document)) { - var context = BsonSerializationContext.CreateRoot(bsonWriter); + var context = BsonSerializationContext.CreateRoot(bsonWriter, args.SerializationDomain); bsonWriter.WriteStartDocument(); bsonWriter.WriteName(renderedField.FieldName); bsonWriter.WriteStartDocument(); @@ -2551,7 +2551,7 @@ public override BsonDocument Render(RenderArgs args) var document = new BsonDocument(); using (var bsonWriter = new BsonDocumentWriter(document)) { - var context = BsonSerializationContext.CreateRoot(bsonWriter); + var context = BsonSerializationContext.CreateRoot(bsonWriter, args.SerializationDomain); bsonWriter.WriteStartDocument(); bsonWriter.WriteName(renderedField.FieldName); bsonWriter.WriteStartDocument(); @@ -2600,7 +2600,7 @@ public override BsonDocument Render(RenderArgs args) var document = new BsonDocument(); using (var bsonWriter = new BsonDocumentWriter(document)) { - var context = BsonSerializationContext.CreateRoot(bsonWriter); + var context = BsonSerializationContext.CreateRoot(bsonWriter, args.SerializationDomain); bsonWriter.WriteStartDocument(); bsonWriter.WriteName(renderedField.FieldName); itemSerializer.Serialize(context, _value); @@ -2660,7 +2660,7 @@ public override BsonDocument Render(RenderArgs args) var document = new BsonDocument(); using (var bsonWriter = new BsonDocumentWriter(document)) { - var context = BsonSerializationContext.CreateRoot(bsonWriter); + var context = BsonSerializationContext.CreateRoot(bsonWriter, args.SerializationDomain); var stringSerializer = BsonStringSerializer.Instance; var regularExpressionSerializer = BsonRegularExpressionSerializer.Instance; diff --git a/src/MongoDB.Driver/FindFluent.cs b/src/MongoDB.Driver/FindFluent.cs index cc25e841b29..e8f5dfd876c 100644 --- a/src/MongoDB.Driver/FindFluent.cs +++ b/src/MongoDB.Driver/FindFluent.cs @@ -282,7 +282,7 @@ private TRendered Render(Func, TRendered> rende { var args = new RenderArgs( _collection.DocumentSerializer, - _collection.Settings.SerializerRegistry, + _collection.Settings.SerializationDomain, renderForFind: renderForFind, translationOptions: translationOptions); diff --git a/src/MongoDB.Driver/GeoJsonObjectModel/Serializers/GeoJsonBoundingBoxSerializer.cs b/src/MongoDB.Driver/GeoJsonObjectModel/Serializers/GeoJsonBoundingBoxSerializer.cs index 1c35c0eadec..248e816a052 100644 --- a/src/MongoDB.Driver/GeoJsonObjectModel/Serializers/GeoJsonBoundingBoxSerializer.cs +++ b/src/MongoDB.Driver/GeoJsonObjectModel/Serializers/GeoJsonBoundingBoxSerializer.cs @@ -73,7 +73,7 @@ protected override GeoJsonBoundingBox DeserializeValue(BsonDeseria using (var documentReader = new BsonDocumentReader(document)) { - var documentContext = BsonDeserializationContext.CreateRoot(documentReader); + var documentContext = BsonDeserializationContext.CreateRoot(documentReader, context.SerializationDomain); documentReader.ReadStartDocument(); documentReader.ReadName("min"); var min = _coordinatesSerializer.Deserialize(documentContext); @@ -99,7 +99,8 @@ protected override void SerializeValue(BsonSerializationContext context, BsonSer var document = new BsonDocument(); using (var documentWriter = new BsonDocumentWriter(document)) { - var documentContext = BsonSerializationContext.CreateRoot(documentWriter); + var documentContext = + BsonSerializationContext.CreateRoot(documentWriter, BsonSerializer.DefaultSerializationDomain); //FP Is this correct?; documentWriter.WriteStartDocument(); documentWriter.WriteName("min"); _coordinatesSerializer.Serialize(documentContext, value.Min); diff --git a/src/MongoDB.Driver/GeoJsonObjectModel/Serializers/GeoJsonPointSerializer.cs b/src/MongoDB.Driver/GeoJsonObjectModel/Serializers/GeoJsonPointSerializer.cs index cf28467eff8..821789af86c 100644 --- a/src/MongoDB.Driver/GeoJsonObjectModel/Serializers/GeoJsonPointSerializer.cs +++ b/src/MongoDB.Driver/GeoJsonObjectModel/Serializers/GeoJsonPointSerializer.cs @@ -31,7 +31,7 @@ private static class Flags } // private fields - private readonly IBsonSerializer _coordinatesSerializer = BsonSerializer.LookupSerializer(); + private readonly IBsonSerializer _coordinatesSerializer = BsonSerializer.LookupSerializer(); //QUESTION What do we do? We could lazily initialize it when we get the serialization/deserialization context. private readonly GeoJsonObjectSerializerHelper _helper; // constructors diff --git a/src/MongoDB.Driver/GeoJsonObjectModel/Serializers/GeoJsonPolygonSerializer.cs b/src/MongoDB.Driver/GeoJsonObjectModel/Serializers/GeoJsonPolygonSerializer.cs index 7a204283f47..a973324c7ea 100644 --- a/src/MongoDB.Driver/GeoJsonObjectModel/Serializers/GeoJsonPolygonSerializer.cs +++ b/src/MongoDB.Driver/GeoJsonObjectModel/Serializers/GeoJsonPolygonSerializer.cs @@ -32,6 +32,7 @@ private static class Flags // private fields private readonly IBsonSerializer> _coordinatesSerializer = BsonSerializer.LookupSerializer>(); + //QUESTION This happens in all GeoJsonOBjectModel serializers, what do we do here? Do we just lookup the serialzer in each Serialize/Deserialize or do we cache it? private readonly GeoJsonObjectSerializerHelper _helper; // constructors diff --git a/src/MongoDB.Driver/GridFS/GridFSBucket.cs b/src/MongoDB.Driver/GridFS/GridFSBucket.cs index 7bd029044b8..3f5916f6180 100644 --- a/src/MongoDB.Driver/GridFS/GridFSBucket.cs +++ b/src/MongoDB.Driver/GridFS/GridFSBucket.cs @@ -599,7 +599,7 @@ internal CreateIndexesOperation CreateCreateChunksCollectionIndexesOperation() var collectionNamespace = this.GetChunksCollectionNamespace(); var requests = new[] { new CreateIndexRequest(new BsonDocument { { "files_id", 1 }, { "n", 1 } }) { Unique = true } }; var messageEncoderSettings = this.GetMessageEncoderSettings(); - return new CreateIndexesOperation(collectionNamespace, requests, messageEncoderSettings) + return new CreateIndexesOperation(collectionNamespace, requests, messageEncoderSettings, _database.Settings.SerializationDomain) { WriteConcern = _options.WriteConcern ?? _database.Settings.WriteConcern }; @@ -610,7 +610,7 @@ internal CreateIndexesOperation CreateCreateFilesCollectionIndexesOperation() var collectionNamespace = this.GetFilesCollectionNamespace(); var requests = new[] { new CreateIndexRequest(new BsonDocument { { "filename", 1 }, { "uploadDate", 1 } }) }; var messageEncoderSettings = this.GetMessageEncoderSettings(); - return new CreateIndexesOperation(collectionNamespace, requests, messageEncoderSettings) + return new CreateIndexesOperation(collectionNamespace, requests, messageEncoderSettings, _database.Settings.SerializationDomain) { WriteConcern = _options.WriteConcern ?? _database.Settings.WriteConcern }; @@ -631,17 +631,17 @@ private BulkMixedWriteOperation CreateDeleteChunksOperation(TFileId id) if (seekable) { - return new GridFSSeekableDownloadStream(this, binding, fileInfo); + return new GridFSSeekableDownloadStream(this, binding, fileInfo, _database.Settings.SerializationDomain); } else { - return new GridFSForwardOnlyDownloadStream(this, binding, fileInfo); + return new GridFSForwardOnlyDownloadStream(this, binding, fileInfo, _database.Settings.SerializationDomain); } } internal DropCollectionOperation CreateDropCollectionOperation(CollectionNamespace collectionNamespace, MessageEncoderSettings messageEncoderSettings) { - return new DropCollectionOperation(collectionNamespace, messageEncoderSettings) + return new DropCollectionOperation(collectionNamespace, messageEncoderSettings, _database.Settings.SerializationDomain) { WriteConcern = _options.WriteConcern ?? _database.Settings.WriteConcern }; @@ -675,14 +675,15 @@ private FindOperation> CreateFindOperation( { var filesCollectionNamespace = this.GetFilesCollectionNamespace(); var messageEncoderSettings = this.GetMessageEncoderSettings(); - var args = new RenderArgs>(_fileInfoSerializer, _options.SerializerRegistry, translationOptions: translationOptions); + var args = new RenderArgs>(_fileInfoSerializer, _database.Settings.SerializationDomain, translationOptions: translationOptions); var renderedFilter = filter.Render(args); var renderedSort = options.Sort == null ? null : options.Sort.Render(args); return new FindOperation>( filesCollectionNamespace, _fileInfoSerializer, - messageEncoderSettings) + messageEncoderSettings, + _database.Settings.SerializationDomain) { AllowDiskUse = options.AllowDiskUse, BatchSize = options.BatchSize, @@ -709,7 +710,8 @@ private FindOperation> CreateGetFileInfoByNameOperation( return new FindOperation>( collectionNamespace, _fileInfoSerializer, - messageEncoderSettings) + messageEncoderSettings, + _database.Settings.SerializationDomain) { Filter = filter, Limit = limit, @@ -729,7 +731,8 @@ private FindOperation> CreateGetFileInfoOperation(TFileI return new FindOperation>( filesCollectionNamespace, _fileInfoSerializer, - messageEncoderSettings) + messageEncoderSettings, + _database.Settings.SerializationDomain) { Filter = filter, Limit = 1, @@ -743,7 +746,7 @@ private FindOperation CreateIsFilesCollectionEmptyOperation() { var filesCollectionNamespace = this.GetFilesCollectionNamespace(); var messageEncoderSettings = this.GetMessageEncoderSettings(); - return new FindOperation(filesCollectionNamespace, BsonDocumentSerializer.Instance, messageEncoderSettings) + return new FindOperation(filesCollectionNamespace, BsonDocumentSerializer.Instance, messageEncoderSettings, _database.Settings.SerializationDomain) { Limit = 1, ReadConcern = GetReadConcern(), @@ -756,7 +759,7 @@ private FindOperation CreateIsFilesCollectionEmptyOperation() private ListIndexesOperation CreateListIndexesOperation(CollectionNamespace collectionNamespace) { var messageEncoderSettings = this.GetMessageEncoderSettings(); - return new ListIndexesOperation(collectionNamespace, messageEncoderSettings) + return new ListIndexesOperation(collectionNamespace, messageEncoderSettings, _database.Settings.SerializationDomain) { RetryRequested = _database.Client.Settings.RetryReads }; @@ -822,7 +825,7 @@ private GridFSUploadStream CreateUploadStream(IReadWriteBindingHandle b private void DownloadToStreamHelper(IReadBindingHandle binding, GridFSFileInfo fileInfo, Stream destination, GridFSDownloadOptions options, CancellationToken cancellationToken = default(CancellationToken)) { var retryReads = _database.Client.Settings.RetryReads; - using (var source = new GridFSForwardOnlyDownloadStream(this, binding.Fork(), fileInfo) { RetryReads = retryReads }) + using (var source = new GridFSForwardOnlyDownloadStream(this, binding.Fork(), fileInfo, _database.Settings.SerializationDomain) { RetryReads = retryReads }) { var count = source.Length; var buffer = new byte[fileInfo.ChunkSizeBytes]; @@ -841,7 +844,7 @@ private GridFSUploadStream CreateUploadStream(IReadWriteBindingHandle b private async Task DownloadToStreamHelperAsync(IReadBindingHandle binding, GridFSFileInfo fileInfo, Stream destination, GridFSDownloadOptions options, CancellationToken cancellationToken = default(CancellationToken)) { var retryReads = _database.Client.Settings.RetryReads; - using (var source = new GridFSForwardOnlyDownloadStream(this, binding.Fork(), fileInfo) { RetryReads = retryReads }) + using (var source = new GridFSForwardOnlyDownloadStream(this, binding.Fork(), fileInfo, _database.Settings.SerializationDomain) { RetryReads = retryReads }) { var count = source.Length; var buffer = new byte[fileInfo.ChunkSizeBytes]; diff --git a/src/MongoDB.Driver/GridFS/GridFSBucketCompat.cs b/src/MongoDB.Driver/GridFS/GridFSBucketCompat.cs index cf4d994e11c..f5b6e83973e 100644 --- a/src/MongoDB.Driver/GridFS/GridFSBucketCompat.cs +++ b/src/MongoDB.Driver/GridFS/GridFSBucketCompat.cs @@ -120,8 +120,8 @@ public GridFSBucket(IMongoDatabase database, GridFSBucketOptions options = null) { Ensure.IsNotNull(filter, nameof(filter)); var translationOptions = Database.Client.Settings.TranslationOptions; - var wrappedFilter = WrapFilter(filter, translationOptions); - var wrappedOptions = WrapFindOptions(options, translationOptions); + var wrappedFilter = WrapFilter(filter, Database.Settings.SerializationDomain, translationOptions); + var wrappedOptions = WrapFindOptions(options, Database.Settings.SerializationDomain, translationOptions); var cursor = base.Find(wrappedFilter, wrappedOptions, cancellationToken); return new BatchTransformingAsyncCursor, GridFSFileInfo>(cursor, TransformFileInfos); } @@ -131,8 +131,8 @@ public GridFSBucket(IMongoDatabase database, GridFSBucketOptions options = null) { Ensure.IsNotNull(filter, nameof(filter)); var translationOptions = Database.Client.Settings.TranslationOptions; - var wrappedFilter = WrapFilter(filter, translationOptions); - var wrappedOptions = WrapFindOptions(options, translationOptions); + var wrappedFilter = WrapFilter(filter, Database.Settings.SerializationDomain, translationOptions); + var wrappedOptions = WrapFindOptions(options, Database.Settings.SerializationDomain, translationOptions); var cursor = await base.FindAsync(wrappedFilter, wrappedOptions, cancellationToken).ConfigureAwait(false); return new BatchTransformingAsyncCursor, GridFSFileInfo>(cursor, TransformFileInfos); } @@ -240,17 +240,17 @@ private IEnumerable TransformFileInfos(IEnumerable new GridFSFileInfo(fi.BackingDocument)); } - private FilterDefinition> WrapFilter(FilterDefinition filter, ExpressionTranslationOptions translationOptions) + private FilterDefinition> WrapFilter(FilterDefinition filter, IBsonSerializationDomain serializationDomain, ExpressionTranslationOptions translationOptions) { - var renderedFilter = filter.Render(new(GridFSFileInfoSerializer.Instance, BsonSerializer.SerializerRegistry, translationOptions: translationOptions)); + var renderedFilter = filter.Render(new(GridFSFileInfoSerializer.Instance, serializationDomain, translationOptions: translationOptions)); return new BsonDocumentFilterDefinition>(renderedFilter); } - private GridFSFindOptions WrapFindOptions(GridFSFindOptions options, ExpressionTranslationOptions translationOptions) + private GridFSFindOptions WrapFindOptions(GridFSFindOptions options, IBsonSerializationDomain serializationDomain, ExpressionTranslationOptions translationOptions) { if (options != null) { - var renderedSort = options.Sort == null ? null : options.Sort.Render(new(GridFSFileInfoSerializer.Instance, BsonSerializer.SerializerRegistry, translationOptions: translationOptions)); + var renderedSort = options.Sort == null ? null : options.Sort.Render(new(GridFSFileInfoSerializer.Instance, serializationDomain, translationOptions: translationOptions)); var wrappedSort = renderedSort == null ? null : new BsonDocumentSortDefinition>(renderedSort); return new GridFSFindOptions { diff --git a/src/MongoDB.Driver/GridFS/GridFSBucketOptions.cs b/src/MongoDB.Driver/GridFS/GridFSBucketOptions.cs index 1ef76936f0f..24c16e3d262 100644 --- a/src/MongoDB.Driver/GridFS/GridFSBucketOptions.cs +++ b/src/MongoDB.Driver/GridFS/GridFSBucketOptions.cs @@ -243,7 +243,8 @@ public ReadPreference ReadPreference /// public IBsonSerializerRegistry SerializerRegistry { - get { return BsonSerializer.SerializerRegistry; } + get { return BsonSerializer.SerializerRegistry; } //QUESTION What do we do in this case...? Given it's read only, we could ignore it? Given this is used for default serialization, it should be ok. + //Also, why the immutable options have a serializer registry and the non-immutable options do not? } /// diff --git a/src/MongoDB.Driver/GridFS/GridFSDownloadStreamBase.cs b/src/MongoDB.Driver/GridFS/GridFSDownloadStreamBase.cs index 4e2089eb4eb..f12ff4ee795 100644 --- a/src/MongoDB.Driver/GridFS/GridFSDownloadStreamBase.cs +++ b/src/MongoDB.Driver/GridFS/GridFSDownloadStreamBase.cs @@ -17,7 +17,9 @@ using System.IO; using System.Threading; using System.Threading.Tasks; +using MongoDB.Bson.Serialization; using MongoDB.Driver.Core.Bindings; +using MongoDB.Driver.Core.Misc; namespace MongoDB.Driver.GridFS { @@ -30,15 +32,29 @@ internal abstract class GridFSDownloadStreamBase : GridFSDownloadStream private bool _disposed; private readonly GridFSFileInfo _fileInfo; + // protected fields + protected readonly IBsonSerializationDomain SerializationDomain; + // constructors protected GridFSDownloadStreamBase( IGridFSBucket bucket, IReadBinding binding, - GridFSFileInfo fileInfo) + GridFSFileInfo fileInfo, + IBsonSerializationDomain serializationDomain) { _bucket = bucket; _binding = binding; _fileInfo = fileInfo; + SerializationDomain = serializationDomain; + } + + //EXIT + protected GridFSDownloadStreamBase( + IGridFSBucket bucket, + IReadBinding binding, + GridFSFileInfo fileInfo) + : this(bucket, binding, fileInfo, BsonSerializer.DefaultSerializationDomain) + { } // public properties diff --git a/src/MongoDB.Driver/GridFS/GridFSFileInfoSerializer.cs b/src/MongoDB.Driver/GridFS/GridFSFileInfoSerializer.cs index 4c91bb90283..38e6631c116 100644 --- a/src/MongoDB.Driver/GridFS/GridFSFileInfoSerializer.cs +++ b/src/MongoDB.Driver/GridFS/GridFSFileInfoSerializer.cs @@ -31,7 +31,7 @@ public class GridFSFileInfoSerializer : BsonDocumentBackedClassSerializ /// Initializes a new instance of the class. /// public GridFSFileInfoSerializer() - : this(BsonSerializer.LookupSerializer()) + : this(BsonSerializer.LookupSerializer()) //FP I think this should be fine. { } diff --git a/src/MongoDB.Driver/GridFS/GridFSForwardOnlyDownloadStream.cs b/src/MongoDB.Driver/GridFS/GridFSForwardOnlyDownloadStream.cs index 23f5e48f877..3188cf01fdd 100644 --- a/src/MongoDB.Driver/GridFS/GridFSForwardOnlyDownloadStream.cs +++ b/src/MongoDB.Driver/GridFS/GridFSForwardOnlyDownloadStream.cs @@ -48,8 +48,9 @@ internal class GridFSForwardOnlyDownloadStream : GridFSDownloadStreamBa public GridFSForwardOnlyDownloadStream( GridFSBucket bucket, IReadBinding binding, - GridFSFileInfo fileInfo) - : base(bucket, binding, fileInfo) + GridFSFileInfo fileInfo, + IBsonSerializationDomain serializationDomain) + : base(bucket, binding, fileInfo, serializationDomain) { _lastChunkNumber = (int)((fileInfo.Length - 1) / fileInfo.ChunkSizeBytes); _lastChunkSize = (int)(fileInfo.Length % fileInfo.ChunkSizeBytes); @@ -185,7 +186,8 @@ private FindOperation CreateFirstBatchOperation() return new FindOperation( chunksCollectionNamespace, BsonDocumentSerializer.Instance, - messageEncoderSettings) + messageEncoderSettings, + SerializationDomain) { Filter = filter, Sort = sort, diff --git a/src/MongoDB.Driver/GridFS/GridFSSeekableDownloadStream.cs b/src/MongoDB.Driver/GridFS/GridFSSeekableDownloadStream.cs index 843412df7ef..4a8ba384ca4 100644 --- a/src/MongoDB.Driver/GridFS/GridFSSeekableDownloadStream.cs +++ b/src/MongoDB.Driver/GridFS/GridFSSeekableDownloadStream.cs @@ -40,14 +40,24 @@ internal class GridFSSeekableDownloadStream : GridFSDownloadStreamBase< public GridFSSeekableDownloadStream( GridFSBucket bucket, IReadBinding binding, - GridFSFileInfo fileInfo) - : base(bucket, binding, fileInfo) + GridFSFileInfo fileInfo, + IBsonSerializationDomain serializationDomain) + : base(bucket, binding, fileInfo, serializationDomain) { var idSerializer = bucket.Options.SerializerRegistry.GetSerializer(); var idSerializationInfo = new BsonSerializationInfo("_id", idSerializer, typeof(TFileId)); _idAsBsonValue = idSerializationInfo.SerializeValue(fileInfo.Id); } + //EXIT + public GridFSSeekableDownloadStream( + GridFSBucket bucket, + IReadBinding binding, + GridFSFileInfo fileInfo) + : this(bucket, binding, fileInfo, BsonSerializer.DefaultSerializationDomain) + { + } + // public properties public override bool CanSeek { @@ -160,7 +170,8 @@ private FindOperation CreateGetChunkOperation(long n) return new FindOperation( chunksCollectionNamespace, BsonDocumentSerializer.Instance, - messageEncoderSettings) + messageEncoderSettings, + SerializationDomain) { Filter = filter, Limit = -1, diff --git a/src/MongoDB.Driver/IInheritableMongoClientSettings.cs b/src/MongoDB.Driver/IInheritableMongoClientSettings.cs index 04eb99990f3..86b5854fee3 100644 --- a/src/MongoDB.Driver/IInheritableMongoClientSettings.cs +++ b/src/MongoDB.Driver/IInheritableMongoClientSettings.cs @@ -16,6 +16,7 @@ using System; using System.Text; using MongoDB.Bson; +using MongoDB.Bson.Serialization; namespace MongoDB.Driver { @@ -24,6 +25,7 @@ internal interface IInheritableMongoClientSettings ReadConcern ReadConcern { get; } UTF8Encoding ReadEncoding { get; } ReadPreference ReadPreference { get; } + IBsonSerializationDomain SerializationDomain { get; set; } WriteConcern WriteConcern { get; } UTF8Encoding WriteEncoding { get; } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/GroupingWithOutputExpressionStageDefinitions.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/GroupingWithOutputExpressionStageDefinitions.cs index 4d62eaea95c..565cf37e6c9 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/GroupingWithOutputExpressionStageDefinitions.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/GroupingWithOutputExpressionStageDefinitions.cs @@ -42,9 +42,8 @@ public GroupingWithOutputExpressionStageDefinition(Expression Render(RenderArgs args) { var inputSerializer = args.DocumentSerializer; - var serializerRegistry = args.SerializerRegistry; - var groupingStage = RenderGroupingStage(inputSerializer, serializerRegistry, args.TranslationOptions, out var groupingSerializer); - var projectStage = RenderProjectStage(groupingSerializer, serializerRegistry, args.TranslationOptions, out var outputSerializer); + var groupingStage = RenderGroupingStage(inputSerializer, args.SerializationDomain, args.TranslationOptions, out var groupingSerializer); + var projectStage = RenderProjectStage(groupingSerializer, args.SerializationDomain, args.TranslationOptions, out var outputSerializer); var optimizedStages = OptimizeGroupingStages(groupingStage, projectStage, inputSerializer, outputSerializer); var renderedStages = optimizedStages.Select(x => x.Render().AsBsonDocument); @@ -53,18 +52,18 @@ public override RenderedPipelineStageDefinition Render(RenderArgs inputSerializer, - IBsonSerializerRegistry serializerRegistry, + IBsonSerializationDomain serializationDomain, ExpressionTranslationOptions translationOptions, out IBsonSerializer groupingOutputSerializer); private AstStage RenderProjectStage( IBsonSerializer inputSerializer, - IBsonSerializerRegistry serializerRegistry, + IBsonSerializationDomain serializationDomain, ExpressionTranslationOptions translationOptions, out IBsonSerializer outputSerializer) { var partiallyEvaluatedOutput = (Expression>)PartialEvaluator.EvaluatePartially(_output); - var context = TranslationContext.Create(translationOptions); + var context = TranslationContext.Create(translationOptions, serializationDomain); var outputTranslation = ExpressionToAggregationExpressionTranslator.TranslateLambdaBody(context, partiallyEvaluatedOutput, inputSerializer, asRoot: true); var (projectStage, projectSerializer) = ProjectionHelper.CreateProjectStage(outputTranslation); outputSerializer = (IBsonSerializer)projectSerializer; @@ -101,12 +100,12 @@ public BucketWithOutputExpressionStageDefinition( protected override AstStage RenderGroupingStage( IBsonSerializer inputSerializer, - IBsonSerializerRegistry serializerRegistry, + IBsonSerializationDomain serializationDomain, ExpressionTranslationOptions translationOptions, out IBsonSerializer> groupingOutputSerializer) { var partiallyEvaluatedGroupBy = (Expression>)PartialEvaluator.EvaluatePartially(_groupBy); - var context = TranslationContext.Create(translationOptions); + var context = TranslationContext.Create(translationOptions, serializationDomain); var groupByTranslation = ExpressionToAggregationExpressionTranslator.TranslateLambdaBody(context, partiallyEvaluatedGroupBy, inputSerializer, asRoot: true); var valueSerializer = (IBsonSerializer)groupByTranslation.Serializer; @@ -145,12 +144,12 @@ public BucketAutoWithOutputExpressionStageDefinition( protected override AstStage RenderGroupingStage( IBsonSerializer inputSerializer, - IBsonSerializerRegistry serializerRegistry, + IBsonSerializationDomain serializationDomain, ExpressionTranslationOptions translationOptions, out IBsonSerializer, TInput>> groupingOutputSerializer) { var partiallyEvaluatedGroupBy = (Expression>)PartialEvaluator.EvaluatePartially(_groupBy); - var context = TranslationContext.Create(translationOptions); + var context = TranslationContext.Create(translationOptions, serializationDomain); var groupByTranslation = ExpressionToAggregationExpressionTranslator.TranslateLambdaBody(context, partiallyEvaluatedGroupBy, inputSerializer, asRoot: true); var valueSerializer = (IBsonSerializer)groupByTranslation.Serializer; @@ -183,12 +182,12 @@ public GroupWithOutputExpressionStageDefinition( protected override AstStage RenderGroupingStage( IBsonSerializer inputSerializer, - IBsonSerializerRegistry serializerRegistry, + IBsonSerializationDomain serializationDomain, ExpressionTranslationOptions translationOptions, out IBsonSerializer> groupingOutputSerializer) { var partiallyEvaluatedGroupBy = (Expression>)PartialEvaluator.EvaluatePartially(_groupBy); - var context = TranslationContext.Create(translationOptions); + var context = TranslationContext.Create(translationOptions, serializationDomain); var groupByTranslation = ExpressionToAggregationExpressionTranslator.TranslateLambdaBody(context, partiallyEvaluatedGroupBy, inputSerializer, asRoot: true); var pushElements = AstExpression.AccumulatorField("_elements", AstUnaryAccumulatorOperator.Push, AstExpression.RootVar); var groupBySerializer = (IBsonSerializer)groupByTranslation.Serializer; diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/SerializationHelper.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/SerializationHelper.cs index 0b34b0bd7cf..ae6b638f51e 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/SerializationHelper.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/SerializationHelper.cs @@ -222,7 +222,7 @@ public static BsonValue SerializeValue(IBsonSerializer serializer, object value) { writer.WriteStartDocument(); writer.WriteName("_v"); - var context = BsonSerializationContext.CreateRoot(writer); + var context = BsonSerializationContext.CreateRoot(writer, BsonSerializer.DefaultSerializationDomain); //QUESTION Is it ok here to use the default domain? serializer.Serialize(context, value); writer.WriteEndDocument(); } @@ -237,7 +237,7 @@ public static BsonArray SerializeValues(IBsonSerializer itemSerializer, IEnumera writer.WriteStartDocument(); writer.WriteName("_v"); writer.WriteStartArray(); - var context = BsonSerializationContext.CreateRoot(writer); + var context = BsonSerializationContext.CreateRoot(writer, BsonSerializer.DefaultSerializationDomain); //QUESTION Is it ok here to use the default domain? foreach(var value in values) { itemSerializer.Serialize(context, value); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/TypeExtensions.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/TypeExtensions.cs index ccb8f699740..8879dbcc20f 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/TypeExtensions.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Misc/TypeExtensions.cs @@ -212,10 +212,11 @@ public static bool IsEnumOrNullableEnum(this Type type, out Type enumType, out T type.IsNullableEnum(out enumType, out underlyingType); } - public static bool IsNullable(this Type type) - { - return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); - } + // Commented out because there is an identical method in Bson assembly (and also in this assembly...). + // public static bool IsNullable(this Type type) + // { + // return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + // } public static bool IsNullable(this Type type, out Type valueType) { @@ -231,10 +232,11 @@ public static bool IsNullable(this Type type, out Type valueType) } } - public static bool IsNullableEnum(this Type type) - { - return type.IsNullable(out var valueType) && valueType.IsEnum; - } + // Commented out because there is an identical method in Bson assembly. + // public static bool IsNullableEnum(this Type type) + // { + // return type.IsNullable(out var valueType) && valueType.IsEnum; + // } public static bool IsNullableEnum(this Type type, out Type enumType, out Type underlyingType) { diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/MongoQueryProvider.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/MongoQueryProvider.cs index 2717a7e71d7..9156ead8290 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/MongoQueryProvider.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/MongoQueryProvider.cs @@ -64,6 +64,7 @@ internal sealed class MongoQueryProvider : MongoQueryProvider private readonly IMongoDatabase _database; private ExecutableQuery _executedQuery; private readonly IBsonSerializer _pipelineInputSerializer; + private readonly IBsonSerializationDomain _serializationDomain; // constructors public MongoQueryProvider( @@ -74,6 +75,7 @@ public MongoQueryProvider( { _collection = Ensure.IsNotNull(collection, nameof(collection)); _pipelineInputSerializer = collection.DocumentSerializer; + _serializationDomain = collection.Settings?.SerializationDomain ?? BsonSerializer.DefaultSerializationDomain; } public MongoQueryProvider( @@ -84,15 +86,18 @@ public MongoQueryProvider( { _database = Ensure.IsNotNull(database, nameof(database)); _pipelineInputSerializer = NoPipelineInputSerializer.Instance; + _serializationDomain = _database.Settings.SerializationDomain; } internal MongoQueryProvider( IBsonSerializer pipelineInputSerializer, IClientSessionHandle session, - AggregateOptions options) + AggregateOptions options, + IBsonSerializationDomain serializationDomain) : base(session, options) { _pipelineInputSerializer = Ensure.IsNotNull(pipelineInputSerializer, nameof(pipelineInputSerializer)); + _serializationDomain = Ensure.IsNotNull(serializationDomain, nameof(serializationDomain)); } // public properties @@ -101,6 +106,7 @@ internal MongoQueryProvider( public IMongoDatabase Database => _database; public override BsonDocument[] LoggedStages => _executedQuery?.LoggedStages; public override IBsonSerializer PipelineInputSerializer => _pipelineInputSerializer; + public IBsonSerializationDomain SerializationDomain => _serializationDomain; // public methods public override IQueryable CreateQuery(Expression expression) diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/ISetWindowFieldsPartitionSerializer.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/ISetWindowFieldsPartitionSerializer.cs index 2be9f49a1b3..69ccc07e370 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/ISetWindowFieldsPartitionSerializer.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Serializers/ISetWindowFieldsPartitionSerializer.cs @@ -61,13 +61,11 @@ public void Serialize(BsonSerializationContext context, BsonSerializationArgs ar throw new InvalidOperationException("This serializer is not intended to be used."); } - public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) { throw new InvalidOperationException("This serializer is not intended to be used."); } - object IBsonSerializer.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { throw new InvalidOperationException("This serializer is not intended to be used."); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/DiscriminatorAstExpression.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/DiscriminatorAstExpression.cs index 8baf59592fc..9c3b573f1f8 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/DiscriminatorAstExpression.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/DiscriminatorAstExpression.cs @@ -16,6 +16,7 @@ using System; using System.Linq; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Conventions; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions; @@ -23,17 +24,17 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Translators; internal static class DiscriminatorAstExpression { - public static AstExpression TypeEquals(AstGetFieldExpression discriminatorField, IDiscriminatorConvention discriminatorConvention, Type nominalType, Type actualType) + public static AstExpression TypeEquals(AstGetFieldExpression discriminatorField, IDiscriminatorConvention discriminatorConvention, Type nominalType, Type actualType, IBsonSerializationDomain serializationDomain) { - var discriminator = discriminatorConvention.GetDiscriminator(nominalType, actualType); + var discriminator = discriminatorConvention.GetDiscriminatorInternal(nominalType, actualType, serializationDomain); return discriminator == null ? AstExpression.IsMissing(discriminatorField) : AstExpression.Eq(discriminatorField, discriminator); } - public static AstExpression TypeIs(AstGetFieldExpression discriminatorField, IHierarchicalDiscriminatorConvention discriminatorConvention, Type nominalType, Type actualType) + public static AstExpression TypeIs(AstGetFieldExpression discriminatorField, IHierarchicalDiscriminatorConvention discriminatorConvention, Type nominalType, Type actualType, IBsonSerializationDomain serializationDomain) { - var discriminator = discriminatorConvention.GetDiscriminator(nominalType, actualType); + var discriminator = discriminatorConvention.GetDiscriminatorInternal(nominalType, actualType, serializationDomain); var lastItem = discriminator is BsonArray array ? array.Last() : discriminator; return AstExpression.Cond( AstExpression.Eq(AstExpression.Type(discriminatorField), "array"), @@ -41,9 +42,9 @@ public static AstExpression TypeIs(AstGetFieldExpression discriminatorField, IHi AstExpression.Eq(discriminatorField, lastItem)); } - public static AstExpression TypeIs(AstGetFieldExpression discriminatorField, IScalarDiscriminatorConvention discriminatorConvention, Type nominalType, Type actualType) + public static AstExpression TypeIs(AstGetFieldExpression discriminatorField, IScalarDiscriminatorConvention discriminatorConvention, Type nominalType, Type actualType, IBsonSerializationDomain serializationDomain) { - var discriminators = discriminatorConvention.GetDiscriminatorsForTypeAndSubTypes(actualType); + var discriminators = discriminatorConvention.GetDiscriminatorsForTypeAndSubTypesInternal(actualType, serializationDomain); return discriminators.Length == 1 ? AstExpression.Eq(discriminatorField, discriminators.Single()) : AstExpression.In(discriminatorField, new BsonArray(discriminators)); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/DiscriminatorAstFilter.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/DiscriminatorAstFilter.cs index 76149481b73..8e59861785f 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/DiscriminatorAstFilter.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/DiscriminatorAstFilter.cs @@ -16,6 +16,7 @@ using System; using System.Linq; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Conventions; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Linq.Linq3Implementation.Ast.Filters; @@ -24,9 +25,9 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Translators; internal static class DiscriminatorAstFilter { - public static AstFilter TypeEquals(AstFilterField discriminatorField, IHierarchicalDiscriminatorConvention discriminatorConvention, Type nominalType, Type actualType) + public static AstFilter TypeEquals(AstFilterField discriminatorField, IHierarchicalDiscriminatorConvention discriminatorConvention, Type nominalType, Type actualType, IBsonSerializationDomain serializationDomain) { - var discriminator = discriminatorConvention.GetDiscriminator(nominalType, actualType); + var discriminator = discriminatorConvention.GetDiscriminatorInternal(nominalType, actualType, serializationDomain); if (discriminator == null) { return AstFilter.NotExists(discriminatorField); @@ -44,24 +45,24 @@ public static AstFilter TypeEquals(AstFilterField discriminatorField, IHierarchi } } - public static AstFilter TypeEquals(AstFilterField discriminatorField, IDiscriminatorConvention discriminatorConvention, Type nominalType, Type actualType) + public static AstFilter TypeEquals(AstFilterField discriminatorField, IDiscriminatorConvention discriminatorConvention, Type nominalType, Type actualType, IBsonSerializationDomain serializationDomain) { - var discriminator = discriminatorConvention.GetDiscriminator(nominalType, actualType); + var discriminator = discriminatorConvention.GetDiscriminatorInternal(nominalType, actualType, serializationDomain); return discriminator == null ? AstFilter.NotExists(discriminatorField) : AstFilter.Eq(discriminatorField, discriminator); } - public static AstFieldOperationFilter TypeIs(AstFilterField discriminatorField, IHierarchicalDiscriminatorConvention discriminatorConvention, Type nominalType, Type actualType) + public static AstFieldOperationFilter TypeIs(AstFilterField discriminatorField, IHierarchicalDiscriminatorConvention discriminatorConvention, Type nominalType, Type actualType, IBsonSerializationDomain serializationDomain) { - var discriminator = discriminatorConvention.GetDiscriminator(nominalType, actualType); + var discriminator = discriminatorConvention.GetDiscriminatorInternal(nominalType, actualType, serializationDomain); var lastItem = discriminator is BsonArray array ? array.Last() : discriminator; return AstFilter.Eq(discriminatorField, lastItem); // will match subclasses also } - public static AstFieldOperationFilter TypeIs(AstFilterField discriminatorField, IScalarDiscriminatorConvention discriminatorConvention, Type nominalType, Type actualType) + public static AstFieldOperationFilter TypeIs(AstFilterField discriminatorField, IScalarDiscriminatorConvention discriminatorConvention, Type nominalType, Type actualType, IBsonSerializationDomain serializationDomain) { - var discriminators = discriminatorConvention.GetDiscriminatorsForTypeAndSubTypes(actualType); + var discriminators = discriminatorConvention.GetDiscriminatorsForTypeAndSubTypesInternal(actualType, serializationDomain); return discriminators.Length == 1 ? AstFilter.Eq(discriminatorField, discriminators.Single()) : AstFilter.In(discriminatorField, discriminators); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ArrayLengthExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ArrayLengthExpressionToAggregationExpressionTranslator.cs index 4ee3f896769..b7506dd615f 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ArrayLengthExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ArrayLengthExpressionToAggregationExpressionTranslator.cs @@ -28,7 +28,7 @@ public static TranslatedExpression Translate(TranslationContext context, UnaryEx var arrayExpression = expression.Operand; var arrayTranslation = ExpressionToAggregationExpressionTranslator.TranslateEnumerable(context, arrayExpression); var ast = AstExpression.Size(arrayTranslation.Ast); - var serializer = BsonSerializer.LookupSerializer(expression.Type); + var serializer = context.SerializationDomain.LookupSerializer(expression.Type); return new TranslatedExpression(expression, ast, serializer); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConstantExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConstantExpressionToAggregationExpressionTranslator.cs index 7487627213d..2fbeab82308 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConstantExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConstantExpressionToAggregationExpressionTranslator.cs @@ -23,10 +23,10 @@ namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggreg { internal static class ConstantExpressionToAggregationExpressionTranslator { - public static TranslatedExpression Translate(ConstantExpression constantExpression) + public static TranslatedExpression Translate(TranslationContext context, ConstantExpression constantExpression) { var constantType = constantExpression.Type; - var constantSerializer = StandardSerializers.TryGetSerializer(constantType, out var serializer) ? serializer : BsonSerializer.LookupSerializer(constantType); + var constantSerializer = StandardSerializers.TryGetSerializer(constantType, out var serializer) ? serializer : context.SerializationDomain.LookupSerializer(constantType); return Translate(constantExpression, constantSerializer); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConvertExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConvertExpressionToAggregationExpressionTranslator.cs index 532e10c1609..2843eb21327 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConvertExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ConvertExpressionToAggregationExpressionTranslator.cs @@ -44,13 +44,13 @@ sourceExpression is UnaryExpression unarySourceExpression && } var sourceTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, sourceExpression); - return Translate(expression, sourceType, targetType, sourceTranslation); + return Translate(expression, sourceType, targetType, sourceTranslation, context.SerializationDomain); } throw new ExpressionNotSupportedException(expression); } - private static TranslatedExpression Translate(UnaryExpression expression, Type sourceType, Type targetType, TranslatedExpression sourceTranslation) + private static TranslatedExpression Translate(UnaryExpression expression, Type sourceType, Type targetType, TranslatedExpression sourceTranslation, IBsonSerializationDomain serializationDomain) { if (targetType == sourceType) { @@ -60,12 +60,12 @@ private static TranslatedExpression Translate(UnaryExpression expression, Type s // from Nullable must be handled before to Nullable if (IsConvertFromNullableType(sourceType)) { - return TranslateConvertFromNullableType(expression, sourceType, targetType, sourceTranslation); + return TranslateConvertFromNullableType(expression, sourceType, targetType, sourceTranslation, serializationDomain); } if (IsConvertToNullableType(targetType)) { - return TranslateConvertToNullableType(expression, sourceType, targetType, sourceTranslation); + return TranslateConvertToNullableType(expression, sourceType, targetType, sourceTranslation, serializationDomain); } // from here on we know there are no longer any Nullable types involved @@ -97,7 +97,7 @@ private static TranslatedExpression Translate(UnaryExpression expression, Type s if (IsConvertToDerivedType(sourceType, targetType)) { - return TranslateConvertToDerivedType(expression, targetType, sourceTranslation); + return TranslateConvertToDerivedType(expression, targetType, sourceTranslation, serializationDomain); } var ast = sourceTranslation.Ast; @@ -177,10 +177,9 @@ private static TranslatedExpression TranslateConvertToBaseType(UnaryExpression e return new TranslatedExpression(expression, sourceTranslation.Ast, downcastingSerializer); } - private static TranslatedExpression TranslateConvertToDerivedType(UnaryExpression expression, Type targetType, TranslatedExpression sourceTranslation) + private static TranslatedExpression TranslateConvertToDerivedType(UnaryExpression expression, Type targetType, TranslatedExpression sourceTranslation, IBsonSerializationDomain serializationDomain) { - var serializer = BsonSerializer.LookupSerializer(targetType); - + var serializer = serializationDomain.LookupSerializer(targetType); return new TranslatedExpression(expression, sourceTranslation.Ast, serializer); } @@ -218,7 +217,7 @@ private static TranslatedExpression TranslateConvertEnumToUnderlyingType(UnaryEx return new TranslatedExpression(expression, sourceTranslation.Ast, targetSerializer); } - private static TranslatedExpression TranslateConvertFromNullableType(UnaryExpression expression, Type sourceType, Type targetType, TranslatedExpression sourceTranslation) + private static TranslatedExpression TranslateConvertFromNullableType(UnaryExpression expression, Type sourceType, Type targetType, TranslatedExpression sourceTranslation, IBsonSerializationDomain serializationDomain) { if (sourceType.IsNullable(out var sourceValueType)) { @@ -226,7 +225,7 @@ private static TranslatedExpression TranslateConvertFromNullableType(UnaryExpres var sourceNullableSerializer = (INullableSerializer)sourceTranslation.Serializer; var sourceValueSerializer = sourceNullableSerializer.ValueSerializer; var sourceValueTranslation = new TranslatedExpression(expression.Operand, sourceAst, sourceValueSerializer); - var convertTranslation = Translate(expression, sourceValueType, targetType, sourceValueTranslation); + var convertTranslation = Translate(expression, sourceValueType, targetType, sourceValueTranslation, serializationDomain); // note: we would have liked to throw a query execution error here if the value is null and the target type is not nullable but there is no way to do that in MQL // so we just return null instead and the user must check for null themselves if they want to define what happens when the value is null @@ -242,7 +241,7 @@ private static TranslatedExpression TranslateConvertFromNullableType(UnaryExpres throw new ExpressionNotSupportedException(expression, because: "sourceType is not nullable"); } - private static TranslatedExpression TranslateConvertToNullableType(UnaryExpression expression, Type sourceType, Type targetType, TranslatedExpression sourceTranslation) + private static TranslatedExpression TranslateConvertToNullableType(UnaryExpression expression, Type sourceType, Type targetType, TranslatedExpression sourceTranslation, IBsonSerializationDomain serializationDomain) { if (sourceType.IsNullable()) { @@ -252,7 +251,7 @@ private static TranslatedExpression TranslateConvertToNullableType(UnaryExpressi if (targetType.IsNullable(out var targetValueType)) { - var convertTranslation = Translate(expression, sourceType, targetValueType, sourceTranslation); + var convertTranslation = Translate(expression, sourceType, targetValueType, sourceTranslation, serializationDomain); var nullableSerializer = NullableSerializer.Create(convertTranslation.Serializer); return new TranslatedExpression(expression, convertTranslation.Ast, nullableSerializer); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ExpressionToAggregationExpressionTranslator.cs index c2d8e0010e9..5eeb2857f9a 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ExpressionToAggregationExpressionTranslator.cs @@ -67,7 +67,7 @@ public static TranslatedExpression Translate(TranslationContext context, Express case ExpressionType.Conditional: return ConditionalExpressionToAggregationExpressionTranslator.Translate(context, (ConditionalExpression)expression); case ExpressionType.Constant: - return ConstantExpressionToAggregationExpressionTranslator.Translate((ConstantExpression)expression); + return ConstantExpressionToAggregationExpressionTranslator.Translate(context, (ConstantExpression)expression); case ExpressionType.Index: return IndexExpressionToAggregationExpressionTranslator.Translate(context, (IndexExpression)expression); case ExpressionType.ListInit: diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/GetTypeComparisonExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/GetTypeComparisonExpressionToAggregationExpressionTranslator.cs index ba26f5e4279..fdb75dc393d 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/GetTypeComparisonExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/GetTypeComparisonExpressionToAggregationExpressionTranslator.cs @@ -40,9 +40,9 @@ public static TranslatedExpression Translate(TranslationContext context, BinaryE var nominalType = objectExpression.Type; var actualType = comparandType; - var discriminatorConvention = objectTranslation.Serializer.GetDiscriminatorConvention(); + var discriminatorConvention = objectTranslation.Serializer.GetDiscriminatorConvention(context.SerializationDomain); var discriminatorField = AstExpression.GetField(objectTranslation.Ast, discriminatorConvention.ElementName); - var ast = DiscriminatorAstExpression.TypeEquals(discriminatorField, discriminatorConvention, nominalType, actualType); + var ast = DiscriminatorAstExpression.TypeEquals(discriminatorField, discriminatorConvention, nominalType, actualType, context.SerializationDomain); return new TranslatedExpression(expression, ast, BooleanSerializer.Instance); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MemberInitExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MemberInitExpressionToAggregationExpressionTranslator.cs index 20f7e81312c..de959f5dc37 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MemberInitExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MemberInitExpressionToAggregationExpressionTranslator.cs @@ -70,7 +70,7 @@ public static TranslatedExpression Translate( var constructorArgumentExpression = constructorArguments[i]; var constructorArgumentTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, constructorArgumentExpression); var constructorArgumentType = constructorArgumentExpression.Type; - var constructorArgumentSerializer = constructorArgumentTranslation.Serializer ?? BsonSerializer.LookupSerializer(constructorArgumentType); + var constructorArgumentSerializer = constructorArgumentTranslation.Serializer ?? context.SerializationDomain.LookupSerializer(constructorArgumentType); var memberMap = EnsureMemberMap(expression, classMap, creatorMapParameter); EnsureDefaultValue(memberMap); var memberSerializer = CoerceSourceSerializerToMemberSerializer(memberMap, constructorArgumentSerializer); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/CeilingMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/CeilingMethodToAggregationExpressionTranslator.cs index fa0aca10c72..7be49cff330 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/CeilingMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/CeilingMethodToAggregationExpressionTranslator.cs @@ -36,7 +36,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC var argumentAst = ConvertHelper.RemoveWideningConvert(argumentTranslation); var ast = AstExpression.Ceil(argumentAst); - var serializer = BsonSerializer.LookupSerializer(expression.Type); + var serializer = context.SerializationDomain.LookupSerializer(expression.Type); return new TranslatedExpression(expression, ast, serializer); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConstantMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConstantMethodToAggregationExpressionTranslator.cs index 0d977502d8a..47b39ab3e75 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConstantMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ConstantMethodToAggregationExpressionTranslator.cs @@ -40,7 +40,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC { var representationExpression = arguments[1]; var representation = representationExpression.GetConstantValue(expression); - var registeredSerializer = BsonSerializer.LookupSerializer(valueExpression.Type); + var registeredSerializer = context.SerializationDomain.LookupSerializer(valueExpression.Type); if (registeredSerializer is IRepresentationConfigurable representationConfigurableSerializer) { serializer = representationConfigurableSerializer.WithRepresentation(representation); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/FloorMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/FloorMethodToAggregationExpressionTranslator.cs index 7af7b22ae97..ac811564c54 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/FloorMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/FloorMethodToAggregationExpressionTranslator.cs @@ -36,7 +36,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC var argumentAst = ConvertHelper.RemoveWideningConvert(argumentTranslation); var ast = AstExpression.Floor(argumentAst); - var serializer = BsonSerializer.LookupSerializer(expression.Type); + var serializer = context.SerializationDomain.LookupSerializer(expression.Type); return new TranslatedExpression(expression, ast, serializer); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/OfTypeMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/OfTypeMethodToAggregationExpressionTranslator.cs index 5ce6bbe01f1..bad75106f3b 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/OfTypeMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/OfTypeMethodToAggregationExpressionTranslator.cs @@ -56,7 +56,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC var nominalType = itemSerializer.ValueType; var nominalTypeSerializer = itemSerializer; var actualType = method.GetGenericArguments().Single(); - var actualTypeSerializer = BsonSerializer.LookupSerializer(actualType); + var actualTypeSerializer = context.SerializationDomain.LookupSerializer(actualType); AstExpression ast; if (nominalType == actualType) @@ -65,14 +65,14 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC } else { - var discriminatorConvention = nominalTypeSerializer.GetDiscriminatorConvention(); + var discriminatorConvention = nominalTypeSerializer.GetDiscriminatorConvention(context.SerializationDomain); var itemVar = AstExpression.Var("item"); var discriminatorField = AstExpression.GetField(itemVar, discriminatorConvention.ElementName); var ofTypeExpression = discriminatorConvention switch { - IHierarchicalDiscriminatorConvention hierarchicalDiscriminatorConvention => DiscriminatorAstExpression.TypeIs(discriminatorField, hierarchicalDiscriminatorConvention, nominalType, actualType), - IScalarDiscriminatorConvention scalarDiscriminatorConvention => DiscriminatorAstExpression.TypeIs(discriminatorField, scalarDiscriminatorConvention, nominalType, actualType), + IHierarchicalDiscriminatorConvention hierarchicalDiscriminatorConvention => DiscriminatorAstExpression.TypeIs(discriminatorField, hierarchicalDiscriminatorConvention, nominalType, actualType, context.SerializationDomain), + IScalarDiscriminatorConvention scalarDiscriminatorConvention => DiscriminatorAstExpression.TypeIs(discriminatorField, scalarDiscriminatorConvention, nominalType, actualType, context.SerializationDomain), _ => throw new ExpressionNotSupportedException(expression, because: "OfType is not supported with the configured discriminator convention") }; diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/PickMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/PickMethodToAggregationExpressionTranslator.cs index ae21565dc68..59e6d27737e 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/PickMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/PickMethodToAggregationExpressionTranslator.cs @@ -115,7 +115,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC { var sortByExpression = arguments[1]; var sortByDefinition = GetSortByDefinition(sortByExpression, expression); - sortBy = TranslateSortByDefinition(expression, sortByExpression, sortByDefinition, itemSerializer, context.TranslationOptions); + sortBy = TranslateSortByDefinition(context, expression, sortByExpression, sortByDefinition, itemSerializer); } var selectorLambda = (LambdaExpression)GetSelectorArgument(method, arguments); @@ -274,27 +274,26 @@ keyTranslation.Ast is AstGetFieldExpression getFieldExpression && } private static AstSortFields TranslateSortByDefinition( + TranslationContext context, Expression expression, Expression sortByExpression, object sortByDefinition, - IBsonSerializer documentSerializer, - ExpressionTranslationOptions translationOptions) + IBsonSerializer documentSerializer) { var methodInfoDefinition = typeof(PickMethodToAggregationExpressionTranslator).GetMethod(nameof(TranslateSortByDefinitionGeneric), BindingFlags.Static | BindingFlags.NonPublic); var documentType = documentSerializer.ValueType; var methodInfo = methodInfoDefinition.MakeGenericMethod(documentType); - return (AstSortFields)methodInfo.Invoke(null, new object[] { expression, sortByExpression, sortByDefinition, documentSerializer, translationOptions }); + return (AstSortFields)methodInfo.Invoke(null, new object[] { context, expression, sortByExpression, sortByDefinition, documentSerializer }); } private static AstSortFields TranslateSortByDefinitionGeneric( + TranslationContext context, Expression expression, Expression sortByExpression, SortDefinition sortByDefinition, - IBsonSerializer documentSerializer, - ExpressionTranslationOptions translationOptions) + IBsonSerializer documentSerializer) { - var serializerRegistry = BsonSerializer.SerializerRegistry; - var sortDocument = sortByDefinition.Render(new(documentSerializer, serializerRegistry, translationOptions: translationOptions)); + var sortDocument = sortByDefinition.Render(new(documentSerializer, context.SerializationDomain, translationOptions: context.TranslationOptions)); var fields = new List(); foreach (var element in sortDocument) { diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/StandardDeviationMethodsToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/StandardDeviationMethodsToAggregationExpressionTranslator.cs index d0f9079dcd9..99ab331d70f 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/StandardDeviationMethodsToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/StandardDeviationMethodsToAggregationExpressionTranslator.cs @@ -48,12 +48,12 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC input: sourceTranslation.Ast, @as: selectorParameterSymbol.Var, @in: selectorTranslation.Ast); - var selectorResultSerializer = BsonSerializer.LookupSerializer(selectorLambda.ReturnType); + var selectorResultSerializer = context.SerializationDomain.LookupSerializer(selectorLambda.ReturnType); sourceTranslation = new TranslatedExpression(selectorLambda, selectorAst, selectorResultSerializer); } var ast = AstExpression.StdDev(stddevOperator, sourceTranslation.Ast); - var serializer = BsonSerializer.LookupSerializer(expression.Type); + var serializer = context.SerializationDomain.LookupSerializer(expression.Type); return new TranslatedExpression(expression, ast, serializer); } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/WindowMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/WindowMethodToAggregationExpressionTranslator.cs index 9b54a5fdd18..0f6737910bc 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/WindowMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/WindowMethodToAggregationExpressionTranslator.cs @@ -281,7 +281,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC { var @operator = GetNullaryWindowOperator(method); var ast = AstExpression.NullaryWindowExpression(@operator, window); - var serializer = BsonSerializer.LookupSerializer(method.ReturnType); // TODO: use correct serializer + var serializer = context.SerializationDomain.LookupSerializer(method.ReturnType); // TODO: use correct serializer return new TranslatedExpression(expression, ast, serializer); } @@ -296,7 +296,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC ThrowIfSelectorTranslationIsNull(selectorTranslation); var @operator = GetUnaryWindowOperator(method); var ast = AstExpression.UnaryWindowExpression(@operator, selectorTranslation.Ast, window); - var serializer = BsonSerializer.LookupSerializer(method.ReturnType); // TODO: use correct serializer + var serializer = context.SerializationDomain.LookupSerializer(method.ReturnType); // TODO: use correct serializer return new TranslatedExpression(expression, ast, serializer); } @@ -309,7 +309,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC var @operator = GetBinaryWindowOperator(method); var ast = AstExpression.BinaryWindowExpression(@operator, selector1Translation.Ast, selector2Translation.Ast, window); - var serializer = BsonSerializer.LookupSerializer(method.ReturnType); // TODO: use correct serializer + var serializer = context.SerializationDomain.LookupSerializer(method.ReturnType); // TODO: use correct serializer return new TranslatedExpression(expression, ast, serializer); } @@ -324,7 +324,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC var @operator = GetDerivativeOrIntegralWindowOperator(method); var ast = AstExpression.DerivativeOrIntegralWindowExpression(@operator, selectorTranslation.Ast, unit, window); - var serializer = BsonSerializer.LookupSerializer(method.ReturnType); // TODO: use correct serializer + var serializer = context.SerializationDomain.LookupSerializer(method.ReturnType); // TODO: use correct serializer return new TranslatedExpression(expression, ast, serializer); } @@ -335,7 +335,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC var weighting = weightingExpression.GetConstantValue(expression); var ast = AstExpression.ExponentialMovingAverageWindowExpression(selectorTranslation.Ast, weighting, window); - var serializer = BsonSerializer.LookupSerializer(method.ReturnType); // TODO: use correct serializer + var serializer = context.SerializationDomain.LookupSerializer(method.ReturnType); // TODO: use correct serializer return new TranslatedExpression(expression, ast, serializer); } @@ -354,7 +354,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC } var ast = AstExpression.ShiftWindowExpression(selectorTranslation.Ast, by, defaultValue); - var serializer = BsonSerializer.LookupSerializer(method.ReturnType); // TODO: use correct serializer + var serializer = context.SerializationDomain.LookupSerializer(method.ReturnType); // TODO: use correct serializer return new TranslatedExpression(expression, ast, serializer); } } @@ -460,15 +460,14 @@ private static AstWindow TranslateWindow(TranslationContext context, Expression { var windowConstant = windowExpression.GetConstantValue(expression); var sortBy = context.Data?.GetValueOrDefault("SortBy", null); - var serializerRegistry = context.Data?.GetValueOrDefault("SerializerRegistry", null); - return ToAstWindow(windowConstant, sortBy, inputSerializer, serializerRegistry, context.TranslationOptions); + return ToAstWindow(windowConstant, sortBy, inputSerializer, context.SerializationDomain, context.TranslationOptions); } private static AstWindow ToAstWindow( SetWindowFieldsWindow window, object sortBy, IBsonSerializer inputSerializer, - BsonSerializerRegistry serializerRegistry, + IBsonSerializationDomain serializationDomain, ExpressionTranslationOptions translationOptions) { if (window == null) @@ -495,7 +494,7 @@ private static AstWindow ToAstWindow( IBsonSerializer upperBoundaryValueSerializer = null; if (lowerValueBoundary != null || upperValueBoundary != null) { - var sortBySerializer = GetSortBySerializer(sortBy, inputSerializer, serializerRegistry, translationOptions); + var sortBySerializer = GetSortBySerializer(sortBy, inputSerializer, serializationDomain, translationOptions); if (lowerValueBoundary != null) { lowerBoundaryValueSerializer = ValueRangeWindowBoundaryConvertingValueSerializerFactory.Create(lowerValueBoundary, sortBySerializer); @@ -522,7 +521,7 @@ private static AstWindow ToAstWindow( private static IBsonSerializer GetSortBySerializer( object sortBy, IBsonSerializer inputSerializer, - BsonSerializerRegistry serializerRegistry, + IBsonSerializationDomain serializationDomain, ExpressionTranslationOptions translationOptions) { Ensure.IsNotNull(sortBy, nameof(sortBy)); @@ -534,13 +533,13 @@ private static IBsonSerializer GetSortBySerializer( nameof(GetSortBySerializerGeneric), BindingFlags.NonPublic | BindingFlags.Static); var methodInfo = methodInfoDefinition.MakeGenericMethod(documentType); - return (IBsonSerializer)methodInfo.Invoke(null, new object[] { sortBy, inputSerializer, serializerRegistry, translationOptions }); + return (IBsonSerializer)methodInfo.Invoke(null, new object[] { sortBy, inputSerializer, serializationDomain, translationOptions }); } private static IBsonSerializer GetSortBySerializerGeneric( SortDefinition sortBy, IBsonSerializer documentSerializer, - BsonSerializerRegistry serializerRegistry, + IBsonSerializationDomain serializationDomain, ExpressionTranslationOptions translationOptions) { var directionalSortBy = sortBy as DirectionalSortDefinition; @@ -554,7 +553,7 @@ private static IBsonSerializer GetSortBySerializerGeneric( } var field = directionalSortBy.Field; - var renderedField = field.Render(new(documentSerializer, serializerRegistry, translationOptions: translationOptions)); + var renderedField = field.Render(new(documentSerializer, serializationDomain, translationOptions: translationOptions)); return renderedField.FieldSerializer; } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ZipMethodToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ZipMethodToAggregationExpressionTranslator.cs index 259af4e59c6..b0ea6c8a2c6 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ZipMethodToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ZipMethodToAggregationExpressionTranslator.cs @@ -47,8 +47,8 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC var resultSelectorParameters = resultSelectorLambda.Parameters; var resultSelectorParameter1 = resultSelectorParameters[0]; var resultSelectorParameter2 = resultSelectorParameters[1]; - var resultSelectorSymbol1 = context.CreateSymbol(resultSelectorParameter1, BsonSerializer.LookupSerializer(resultSelectorParameter1.Type)); - var resultSelectorSymbol2 = context.CreateSymbol(resultSelectorParameter2, BsonSerializer.LookupSerializer(resultSelectorParameter2.Type)); + var resultSelectorSymbol1 = context.CreateSymbol(resultSelectorParameter1, context.SerializationDomain.LookupSerializer(resultSelectorParameter1.Type)); + var resultSelectorSymbol2 = context.CreateSymbol(resultSelectorParameter2, context.SerializationDomain.LookupSerializer(resultSelectorParameter2.Type)); var resultSelectorContext = context.WithSymbols(resultSelectorSymbol1, resultSelectorSymbol2); var resultSelectorTranslation = ExpressionToAggregationExpressionTranslator.Translate(resultSelectorContext, resultSelectorLambda.Body); var @as = AstExpression.Var("pair"); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/NewArrayInitExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/NewArrayInitExpressionToAggregationExpressionTranslator.cs index c5eba340536..a2abd8b8e87 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/NewArrayInitExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/NewArrayInitExpressionToAggregationExpressionTranslator.cs @@ -45,7 +45,7 @@ public static TranslatedExpression Translate(TranslationContext context, NewArra var arrayType = expression.Type; var itemType = arrayType.GetElementType(); - itemSerializer ??= BsonSerializer.LookupSerializer(itemType); // if the array is empty itemSerializer will be null + itemSerializer ??= context.SerializationDomain.LookupSerializer(itemType); // if the array is empty itemSerializer will be null var arraySerializerType = typeof(ArraySerializer<>).MakeGenericType(itemType); var arraySerializer = (IBsonSerializer)Activator.CreateInstance(arraySerializerType, itemSerializer); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/TypeIsExpressionToAggregationExpressionTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/TypeIsExpressionToAggregationExpressionTranslator.cs index defda7b972b..07badb1e8c4 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/TypeIsExpressionToAggregationExpressionTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/TypeIsExpressionToAggregationExpressionTranslator.cs @@ -39,13 +39,13 @@ public static TranslatedExpression Translate(TranslationContext context, TypeBin } else { - var discriminatorConvention = objectTranslation.Serializer.GetDiscriminatorConvention(); + var discriminatorConvention = objectTranslation.Serializer.GetDiscriminatorConvention(context.SerializationDomain); var discriminatorField = AstExpression.GetField(objectTranslation.Ast, discriminatorConvention.ElementName); ast = discriminatorConvention switch { - IHierarchicalDiscriminatorConvention hierarchicalDiscriminatorConvention => DiscriminatorAstExpression.TypeIs(discriminatorField, hierarchicalDiscriminatorConvention, nominalType, actualType), - IScalarDiscriminatorConvention scalarDiscriminatorConvention => DiscriminatorAstExpression.TypeIs(discriminatorField, scalarDiscriminatorConvention, nominalType, actualType), + IHierarchicalDiscriminatorConvention hierarchicalDiscriminatorConvention => DiscriminatorAstExpression.TypeIs(discriminatorField, hierarchicalDiscriminatorConvention, nominalType, actualType, context.SerializationDomain), + IScalarDiscriminatorConvention scalarDiscriminatorConvention => DiscriminatorAstExpression.TypeIs(discriminatorField, scalarDiscriminatorConvention, nominalType, actualType, context.SerializationDomain), _ => throw new ExpressionNotSupportedException(expression, because: "is operator is not supported with the configured discriminator convention") }; } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/ExpressionToExecutableQueryTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/ExpressionToExecutableQueryTranslator.cs index b96a193e323..a838b8051f6 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/ExpressionToExecutableQueryTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/ExpressionToExecutableQueryTranslator.cs @@ -31,7 +31,7 @@ public static ExecutableQuery> Translate TranslateScalar Translate(MongoQuer valueAst = AstExpression.GetField(AstExpression.RootVar, "_v"); } var outputValueType = expression.GetResultType(); - var outputValueSerializer = BsonSerializer.LookupSerializer(outputValueType); + var outputValueSerializer = context.SerializationDomain.LookupSerializer(outputValueType); var outputWrappedValueSerializer = WrappedValueSerializer.Create("_v", outputValueSerializer); pipeline = pipeline.AddStages( diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/SumMethodToExecutableQueryTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/SumMethodToExecutableQueryTranslator.cs index 0e44e945fd7..3ee5b352757 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/SumMethodToExecutableQueryTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToExecutableQueryTranslators/SumMethodToExecutableQueryTranslator.cs @@ -135,7 +135,7 @@ public static ExecutableQuery Translate(MongoQuer } var outputValueType = expression.GetResultType(); - var outputValueSerializer = BsonSerializer.LookupSerializer(outputValueType); + var outputValueSerializer = context.SerializationDomain.LookupSerializer(outputValueType); var outputWrappedValueSerializer = WrappedValueSerializer.Create("_v", outputValueSerializer); pipeline = pipeline.AddStages( diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionTranslators/GetTypeComparisonExpressionToFilterTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionTranslators/GetTypeComparisonExpressionToFilterTranslator.cs index bbcddc7e29c..32bc3938907 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionTranslators/GetTypeComparisonExpressionToFilterTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionTranslators/GetTypeComparisonExpressionToFilterTranslator.cs @@ -55,13 +55,13 @@ public static AstFilter Translate( var nominalType = fieldTranslation.Serializer.ValueType; var actualType = typeConstantExpression.GetConstantValue(expression); - var discriminatorConvention = fieldTranslation.Serializer.GetDiscriminatorConvention(); + var discriminatorConvention = fieldTranslation.Serializer.GetDiscriminatorConvention(context.SerializationDomain); var discriminatorField = fieldTranslation.Ast.SubField(discriminatorConvention.ElementName); var filter = discriminatorConvention switch { - IHierarchicalDiscriminatorConvention hierarchicalDiscriminatorConvention => DiscriminatorAstFilter.TypeEquals(discriminatorField, hierarchicalDiscriminatorConvention, nominalType, actualType), - _ => DiscriminatorAstFilter.TypeEquals(discriminatorField, discriminatorConvention, nominalType, actualType), + IHierarchicalDiscriminatorConvention hierarchicalDiscriminatorConvention => DiscriminatorAstFilter.TypeEquals(discriminatorField, hierarchicalDiscriminatorConvention, nominalType, actualType, context.SerializationDomain), + _ => DiscriminatorAstFilter.TypeEquals(discriminatorField, discriminatorConvention, nominalType, actualType, context.SerializationDomain), }; return comparisonOperator switch diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionTranslators/TypeIsExpressionToFilterTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionTranslators/TypeIsExpressionToFilterTranslator.cs index 691e310febd..eed84283da1 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionTranslators/TypeIsExpressionToFilterTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionTranslators/TypeIsExpressionToFilterTranslator.cs @@ -41,13 +41,13 @@ public static AstFilter Translate(TranslationContext context, TypeBinaryExpressi } else { - var discriminatorConvention = fieldTranslation.Serializer.GetDiscriminatorConvention(); + var discriminatorConvention = fieldTranslation.Serializer.GetDiscriminatorConvention(context.SerializationDomain); var discriminatorField = fieldTranslation.Ast.SubField(discriminatorConvention.ElementName); return discriminatorConvention switch { - IHierarchicalDiscriminatorConvention hierarchicalDiscriminatorConvention => DiscriminatorAstFilter.TypeIs(discriminatorField, hierarchicalDiscriminatorConvention, nominalType, actualType), - IScalarDiscriminatorConvention scalarDiscriminatorConvention => DiscriminatorAstFilter.TypeIs(discriminatorField, scalarDiscriminatorConvention, nominalType, actualType), + IHierarchicalDiscriminatorConvention hierarchicalDiscriminatorConvention => DiscriminatorAstFilter.TypeIs(discriminatorField, hierarchicalDiscriminatorConvention, nominalType, actualType, context.SerializationDomain), + IScalarDiscriminatorConvention scalarDiscriminatorConvention => DiscriminatorAstFilter.TypeIs(discriminatorField, scalarDiscriminatorConvention, nominalType, actualType, context.SerializationDomain), _ => throw new ExpressionNotSupportedException(expression, because: "is operator is not supported with the configured discriminator convention") }; } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/MethodTranslators/AllOrAnyMethodToFilterTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/MethodTranslators/AllOrAnyMethodToFilterTranslator.cs index abb59233f3c..157bd2a87c6 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/MethodTranslators/AllOrAnyMethodToFilterTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/MethodTranslators/AllOrAnyMethodToFilterTranslator.cs @@ -120,17 +120,17 @@ public static (TranslatedFilterField, AstFilter) Translate(TranslationContext co var sourceSerializer = fieldTranslation.Serializer; var itemSerializer = ArraySerializerHelper.GetItemSerializer(sourceSerializer); - var discriminatorConvention = itemSerializer.GetDiscriminatorConvention(); + var discriminatorConvention = itemSerializer.GetDiscriminatorConvention(context.SerializationDomain); var discriminatorField = AstFilter.Field(discriminatorConvention.ElementName); var ofTypeFilter = discriminatorConvention switch { - IHierarchicalDiscriminatorConvention hierarchicalDiscriminatorConvention => DiscriminatorAstFilter.TypeIs(discriminatorField, hierarchicalDiscriminatorConvention, nominalType, actualType), - IScalarDiscriminatorConvention scalarDiscriminatorConvention => DiscriminatorAstFilter.TypeIs(discriminatorField, scalarDiscriminatorConvention, nominalType, actualType), + IHierarchicalDiscriminatorConvention hierarchicalDiscriminatorConvention => DiscriminatorAstFilter.TypeIs(discriminatorField, hierarchicalDiscriminatorConvention, nominalType, actualType, context.SerializationDomain), + IScalarDiscriminatorConvention scalarDiscriminatorConvention => DiscriminatorAstFilter.TypeIs(discriminatorField, scalarDiscriminatorConvention, nominalType, actualType, context.SerializationDomain), _ => throw new ExpressionNotSupportedException(sourceExpression, because: "OfType method is not supported with the configured discriminator convention") }; - var actualTypeSerializer = BsonSerializer.LookupSerializer(actualType); + var actualTypeSerializer = context.SerializationDomain.LookupSerializer(actualType); var enumerableActualTypeSerializer = IEnumerableSerializer.Create(actualTypeSerializer); var actualTypeSourceField = new TranslatedFilterField(fieldTranslation.Ast, enumerableActualTypeSerializer); var combinedFilter = AstFilter.Combine(sourceFilter, ofTypeFilter); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/MethodTranslators/InjectMethodToFilterTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/MethodTranslators/InjectMethodToFilterTranslator.cs index a21ed9d567e..67d09688419 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/MethodTranslators/InjectMethodToFilterTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/MethodTranslators/InjectMethodToFilterTranslator.cs @@ -58,10 +58,9 @@ public static AstFilter Translate(TranslationContext context, MethodCallExpressi { throw new ExpressionNotSupportedException(expression, because: $"FilterDefinition TDocument type: {filterDefinitionDocumentType} does not match document type {documentSerializer.ValueType} "); } - var serializerRegistry = BsonSerializer.SerializerRegistry; var renderFilterMethod = __renderFilterMethodInfo.MakeGenericMethod(filterDefinitionDocumentType); - var renderedFilter = (BsonDocument)renderFilterMethod.Invoke(null, new[] { filterDefinition, documentSerializer, serializerRegistry, context.TranslationOptions }); + var renderedFilter = (BsonDocument)renderFilterMethod.Invoke(null, new[] { filterDefinition, documentSerializer, context.SerializationDomain, context.TranslationOptions }); return AstFilter.Raw(renderedFilter); } @@ -73,8 +72,8 @@ public static AstFilter Translate(TranslationContext context, MethodCallExpressi private static BsonDocument RenderFilter( FilterDefinition filterDefinition, IBsonSerializer documentSerializer, - IBsonSerializerRegistry serializerRegistry, + IBsonSerializationDomain serializationDomain, ExpressionTranslationOptions translationOptions) => - filterDefinition.Render(new(documentSerializer, serializerRegistry, translationOptions: translationOptions)); + filterDefinition.Render(new(documentSerializer, serializationDomain, translationOptions: translationOptions)); } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/ConvertExpressionToFilterFieldTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/ConvertExpressionToFilterFieldTranslator.cs index 7a734d7a075..40889bf9aef 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/ConvertExpressionToFilterFieldTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/ConvertExpressionToFilterFieldTranslator.cs @@ -66,7 +66,7 @@ public static TranslatedFilterField Translate(TranslationContext context, UnaryE if (IsConvertToDerivedType(fieldType, targetType)) { - return TranslateConvertToDerivedType(fieldTranslation, targetType); + return TranslateConvertToDerivedType(fieldTranslation, targetType, context.SerializationDomain); } } @@ -165,9 +165,9 @@ private static TranslatedFilterField TranslateConvertToBaseType(TranslatedFilter return new TranslatedFilterField(fieldTranslation.Ast, targetSerializer); } - private static TranslatedFilterField TranslateConvertToDerivedType(TranslatedFilterField fieldTranslation, Type targetType) + private static TranslatedFilterField TranslateConvertToDerivedType(TranslatedFilterField fieldTranslation, Type targetType, IBsonSerializationDomain serializationDomain) { - var targetSerializer = BsonSerializer.LookupSerializer(targetType); + var targetSerializer = serializationDomain.LookupSerializer(targetType); return new TranslatedFilterField(fieldTranslation.Ast, targetSerializer); } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/AppendStageMethodToPipelineTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/AppendStageMethodToPipelineTranslator.cs index f226a6d504f..56e9acd51aa 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/AppendStageMethodToPipelineTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/AppendStageMethodToPipelineTranslator.cs @@ -39,7 +39,7 @@ public static TranslatedPipeline Translate(TranslationContext context, MethodCal var sourceSerializer = pipeline.OutputSerializer; var stageExpression = arguments[1]; - var renderedStage = TranslateStage(expression, stageExpression, sourceSerializer, BsonSerializer.SerializerRegistry, context.TranslationOptions); + var renderedStage = TranslateStage(expression, stageExpression, sourceSerializer, context.SerializationDomain, context.TranslationOptions); var stage = AstStage.Universal(renderedStage.Document); var resultSerializerExpression = arguments[2]; @@ -57,11 +57,11 @@ private static IRenderedPipelineStageDefinition TranslateStage( Expression expression, Expression stageExpression, IBsonSerializer inputSerializer, - IBsonSerializerRegistry serializerRegistry, + IBsonSerializationDomain serializationDomain, ExpressionTranslationOptions translationOptions) { - var stageDefinition = stageExpression.GetConstantValue(stageExpression); - return stageDefinition.Render(inputSerializer, serializerRegistry, translationOptions); + var stageDefinition = stageExpression.GetConstantValue(stageExpression); + return stageDefinition.Render(inputSerializer, serializationDomain, translationOptions); } } } diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/AsMethodToPipelineTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/AsMethodToPipelineTranslator.cs index a7426d8e69c..60bd66c5a59 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/AsMethodToPipelineTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/AsMethodToPipelineTranslator.cs @@ -41,7 +41,7 @@ public static TranslatedPipeline Translate(TranslationContext context, MethodCal var resultSerializerExpression = arguments[1]; var resultSerializer = resultSerializerExpression.GetConstantValue(expression); var resultType = method.GetGenericArguments()[1]; - var outputSerializer = resultSerializer ?? BsonSerializer.LookupSerializer(resultType); + var outputSerializer = resultSerializer ?? context.SerializationDomain.LookupSerializer(resultType); pipeline = pipeline.WithNewOutputSerializer(outputSerializer); return pipeline; diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/ConcatMethodToPipelineTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/ConcatMethodToPipelineTranslator.cs index 03fb1ecb1b7..ccaec5bdc73 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/ConcatMethodToPipelineTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/ConcatMethodToPipelineTranslator.cs @@ -44,7 +44,7 @@ secondProvider.CollectionNamespace is var secondCollectionNamespace && secondCollectionNamespace != null) { var secondCollectionName = secondCollectionNamespace.CollectionName; - var secondContext = TranslationContext.Create(context.TranslationOptions); + var secondContext = TranslationContext.Create(context.TranslationOptions, context.SerializationDomain); var secondPipeline = ExpressionToPipelineTranslator.Translate(secondContext, secondQueryable.Expression); if (secondPipeline.Ast.Stages.Count == 0) { diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/DocumentsMethodToPipelineTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/DocumentsMethodToPipelineTranslator.cs index 6f7ae7a9a3f..c03f0a8725d 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/DocumentsMethodToPipelineTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/DocumentsMethodToPipelineTranslator.cs @@ -58,7 +58,7 @@ public static TranslatedPipeline Translate(TranslationContext context, MethodCal else { var documentType = method.GetGenericArguments()[0]; - documentSerializer = BsonSerializer.LookupSerializer(documentType); + documentSerializer = context.SerializationDomain.LookupSerializer(documentType); } var serializedDocuments = SerializationHelper.SerializeValues(documentSerializer, documents); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/LookupMethodToPipelineTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/LookupMethodToPipelineTranslator.cs index 93916b46c90..bcb31d605ff 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/LookupMethodToPipelineTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/LookupMethodToPipelineTranslator.cs @@ -281,12 +281,12 @@ private static TranslatedPipeline TranslateDocumentsPipelineGeneric(documentSerializer, session: null, options: null); + var provider = new MongoQueryProvider(documentSerializer, session: null, options: null, serializationDomain: context.SerializationDomain); var queryable = new MongoQuery(provider); body = ExpressionReplacer.Replace(body, queryableParameter, Expression.Constant(queryable)); @@ -332,7 +332,7 @@ private static TranslatedPipeline TranslateLookupPipelineAgainstQueryable DiscriminatorAstFilter.TypeIs(discriminatorField, hierarchicalDiscriminatorConvention, nominalType, actualType), - IScalarDiscriminatorConvention scalarDiscriminatorConvention => DiscriminatorAstFilter.TypeIs(discriminatorField, scalarDiscriminatorConvention, nominalType, actualType), + IHierarchicalDiscriminatorConvention hierarchicalDiscriminatorConvention => DiscriminatorAstFilter.TypeIs(discriminatorField, hierarchicalDiscriminatorConvention, nominalType, actualType, context.SerializationDomain), + IScalarDiscriminatorConvention scalarDiscriminatorConvention => DiscriminatorAstFilter.TypeIs(discriminatorField, scalarDiscriminatorConvention, nominalType, actualType, context.SerializationDomain), _ => throw new ExpressionNotSupportedException(expression, because: "OfType is not supported with the configured discriminator convention") }; - var resultSerializer = BsonSerializer.LookupSerializer(actualType); + var resultSerializer = context.SerializationDomain.LookupSerializer(actualType); if (wrappedValueOutputSerializer != null) { resultSerializer = WrappedValueSerializer.Create(wrappedValueOutputSerializer.FieldName, resultSerializer); diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/UnionMethodToPipelineTranslator.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/UnionMethodToPipelineTranslator.cs index 6e2cd53e465..b7734723a6c 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/UnionMethodToPipelineTranslator.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToPipelineTranslators/UnionMethodToPipelineTranslator.cs @@ -46,7 +46,7 @@ secondProvider.CollectionNamespace is var secondCollectionNamespace && secondCollectionNamespace != null) { var secondCollectionName = secondCollectionNamespace.CollectionName; - var secondContext = TranslationContext.Create(context.TranslationOptions); + var secondContext = TranslationContext.Create(context.TranslationOptions, context.SerializationDomain); var secondPipeline = ExpressionToPipelineTranslator.Translate(secondContext, secondQueryable.Expression); if (secondPipeline.Ast.Stages.Count == 0) { diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/TranslationContext.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/TranslationContext.cs index b14afedd614..41e037c2453 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/TranslationContext.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/Translators/TranslationContext.cs @@ -26,17 +26,19 @@ internal class TranslationContext #region static public static TranslationContext Create( ExpressionTranslationOptions translationOptions, + IBsonSerializationDomain serializationDomain, TranslationContextData data = null) { var symbolTable = new SymbolTable(); var nameGenerator = new NameGenerator(); - return new TranslationContext(translationOptions, data, symbolTable, nameGenerator); + return new TranslationContext(translationOptions, data, symbolTable, nameGenerator, serializationDomain); } #endregion // private fields private readonly TranslationContextData _data; private readonly NameGenerator _nameGenerator; + private readonly IBsonSerializationDomain _serializationDomain; private readonly SymbolTable _symbolTable; private readonly ExpressionTranslationOptions _translationOptions; @@ -44,17 +46,21 @@ private TranslationContext( ExpressionTranslationOptions translationOptions, TranslationContextData data, SymbolTable symbolTable, - NameGenerator nameGenerator) + NameGenerator nameGenerator, + IBsonSerializationDomain serializationDomain) { _translationOptions = translationOptions ?? new ExpressionTranslationOptions(); _data = data; // can be null _symbolTable = Ensure.IsNotNull(symbolTable, nameof(symbolTable)); + _serializationDomain = serializationDomain; _nameGenerator = Ensure.IsNotNull(nameGenerator, nameof(nameGenerator)); } // public properties public TranslationContextData Data => _data; public NameGenerator NameGenerator => _nameGenerator; + public IBsonSerializationDomain SerializationDomain => _serializationDomain; + public SymbolTable SymbolTable => _symbolTable; public ExpressionTranslationOptions TranslationOptions => _translationOptions; @@ -124,7 +130,7 @@ public TranslationContext WithSymbols(params Symbol[] newSymbols) public TranslationContext WithSymbolTable(SymbolTable symbolTable) { - return new TranslationContext(_translationOptions, _data, symbolTable, _nameGenerator); + return new TranslationContext(_translationOptions, _data, symbolTable, _nameGenerator, _serializationDomain); } } } diff --git a/src/MongoDB.Driver/Linq/LinqProviderAdapter.cs b/src/MongoDB.Driver/Linq/LinqProviderAdapter.cs index 67ca25b4261..19550d75afc 100644 --- a/src/MongoDB.Driver/Linq/LinqProviderAdapter.cs +++ b/src/MongoDB.Driver/Linq/LinqProviderAdapter.cs @@ -56,12 +56,12 @@ internal static IQueryable AsQueryable( internal static BsonValue TranslateExpressionToAggregateExpression( Expression> expression, IBsonSerializer sourceSerializer, - IBsonSerializerRegistry serializerRegistry, + IBsonSerializationDomain serializationDomain, ExpressionTranslationOptions translationOptions, TranslationContextData contextData = null) { expression = (Expression>)PartialEvaluator.EvaluatePartially(expression); - var context = TranslationContext.Create(translationOptions, contextData); + var context = TranslationContext.Create(translationOptions, serializationDomain, contextData); var translation = ExpressionToAggregationExpressionTranslator.TranslateLambdaBody(context, expression, sourceSerializer, asRoot: true); var simplifiedAst = AstSimplifier.Simplify(translation.Ast); @@ -71,12 +71,12 @@ internal static BsonValue TranslateExpressionToAggregateExpression( LambdaExpression expression, IBsonSerializer documentSerializer, - IBsonSerializerRegistry serializerRegistry, + IBsonSerializationDomain serializationDomain, ExpressionTranslationOptions translationOptions) { expression = (LambdaExpression)PartialEvaluator.EvaluatePartially(expression); var parameter = expression.Parameters.Single(); - var context = TranslationContext.Create(translationOptions); + var context = TranslationContext.Create(translationOptions, serializationDomain); var symbol = context.CreateSymbol(parameter, documentSerializer, isCurrent: true); context = context.WithSymbol(symbol); var body = RemovePossibleConvertToObject(expression.Body); @@ -100,20 +100,20 @@ static Expression RemovePossibleConvertToObject(Expression expression) internal static RenderedFieldDefinition TranslateExpressionToField( Expression> expression, IBsonSerializer documentSerializer, - IBsonSerializerRegistry serializerRegistry, + IBsonSerializationDomain serializationDomain, ExpressionTranslationOptions translationOptions, bool allowScalarValueForArrayField) { expression = (Expression>)PartialEvaluator.EvaluatePartially(expression); var parameter = expression.Parameters.Single(); - var context = TranslationContext.Create(translationOptions); + var context = TranslationContext.Create(translationOptions, serializationDomain); var symbol = context.CreateSymbol(parameter, documentSerializer, isCurrent: true); context = context.WithSymbol(symbol); var fieldTranslation = ExpressionToFilterFieldTranslator.Translate(context, expression.Body); var underlyingSerializer = fieldTranslation.Serializer; var fieldSerializer = underlyingSerializer as IBsonSerializer; - var valueSerializer = (IBsonSerializer)FieldValueSerializerHelper.GetSerializerForValueType(underlyingSerializer, serializerRegistry, typeof(TField), allowScalarValueForArrayField); + var valueSerializer = (IBsonSerializer)FieldValueSerializerHelper.GetSerializerForValueType(underlyingSerializer, serializationDomain.SerializerRegistry, typeof(TField), allowScalarValueForArrayField); return new RenderedFieldDefinition(fieldTranslation.Ast.Path, fieldSerializer, valueSerializer, underlyingSerializer); } @@ -121,11 +121,11 @@ internal static RenderedFieldDefinition TranslateExpressionToField( Expression> expression, IBsonSerializer elementSerializer, - IBsonSerializerRegistry serializerRegistry, + IBsonSerializationDomain serializationDomain, ExpressionTranslationOptions translationOptions) { expression = (Expression>)PartialEvaluator.EvaluatePartially(expression); - var context = TranslationContext.Create(translationOptions); + var context = TranslationContext.Create(translationOptions, serializationDomain); var parameter = expression.Parameters.Single(); var symbol = context.CreateSymbol(parameter, "@", elementSerializer); // @ represents the implied element context = context.WithSingleSymbol(symbol); // @ is the only symbol visible inside an $elemMatch @@ -138,11 +138,11 @@ internal static BsonDocument TranslateExpressionToElemMatchFilter( internal static BsonDocument TranslateExpressionToFilter( Expression> expression, IBsonSerializer documentSerializer, - IBsonSerializerRegistry serializerRegistry, + IBsonSerializationDomain serializationDomain, ExpressionTranslationOptions translationOptions) { expression = (Expression>)PartialEvaluator.EvaluatePartially(expression); - var context = TranslationContext.Create(translationOptions); + var context = TranslationContext.Create(translationOptions, serializationDomain); var filter = ExpressionToFilterTranslator.TranslateLambda(context, expression, documentSerializer, asRoot: true); filter = AstSimplifier.SimplifyAndConvert(filter); @@ -152,22 +152,23 @@ internal static BsonDocument TranslateExpressionToFilter( internal static RenderedProjectionDefinition TranslateExpressionToFindProjection( Expression> expression, IBsonSerializer sourceSerializer, - IBsonSerializerRegistry serializerRegistry, + IBsonSerializationDomain serializationDomain, ExpressionTranslationOptions translationOptions) - => TranslateExpressionToProjection(expression, sourceSerializer, translationOptions, forFind: true); + => TranslateExpressionToProjection(expression, sourceSerializer, translationOptions, forFind: true, serializationDomain); internal static RenderedProjectionDefinition TranslateExpressionToProjection( Expression> expression, IBsonSerializer inputSerializer, - IBsonSerializerRegistry serializerRegistry, + IBsonSerializationDomain serializationDomain, ExpressionTranslationOptions translationOptions) - => TranslateExpressionToProjection(expression, inputSerializer, translationOptions, forFind: false); + => TranslateExpressionToProjection(expression, inputSerializer, translationOptions, forFind: false, serializationDomain); private static RenderedProjectionDefinition TranslateExpressionToProjection( Expression> expression, IBsonSerializer inputSerializer, ExpressionTranslationOptions translationOptions, - bool forFind) + bool forFind, + IBsonSerializationDomain serializationDomain) { if (expression.Parameters.Count == 1 && expression.Body == expression.Parameters[0]) { @@ -176,7 +177,7 @@ private static RenderedProjectionDefinition TranslateExpressionToProjec } expression = (Expression>)PartialEvaluator.EvaluatePartially(expression); - var context = TranslationContext.Create(translationOptions); + var context = TranslationContext.Create(translationOptions, serializationDomain); var simplifier = forFind ? new AstFindProjectionSimplifier() : new AstSimplifier(); try @@ -212,10 +213,10 @@ private static RenderedProjectionDefinition TranslateExpressionToProjec internal static BsonDocument TranslateExpressionToSetStage( Expression> expression, IBsonSerializer documentSerializer, - IBsonSerializerRegistry serializerRegistry, + IBsonSerializationDomain serializationDomain, ExpressionTranslationOptions translationOptions) { - var context = TranslationContext.Create(translationOptions); // do not partially evaluate expression + var context = TranslationContext.Create(translationOptions, serializationDomain); // do not partially evaluate expression var parameter = expression.Parameters.Single(); var symbol = context.CreateRootSymbol(parameter, documentSerializer); context = context.WithSymbol(symbol); diff --git a/src/MongoDB.Driver/MongoClient.cs b/src/MongoDB.Driver/MongoClient.cs index 0df21ba32cc..bf818f13894 100644 --- a/src/MongoDB.Driver/MongoClient.cs +++ b/src/MongoDB.Driver/MongoClient.cs @@ -502,7 +502,7 @@ private ClientBulkWriteOperation CreateClientBulkWriteOperation(IReadOnlyList CreateDatabaseNamesCursor(IAsyncCursor databases.Select(database => database["name"].AsString)); private DropDatabaseOperation CreateDropDatabaseOperation(string name) - => new(new DatabaseNamespace(name), GetMessageEncoderSettings()) + => new(new DatabaseNamespace(name), GetMessageEncoderSettings(), _settings.SerializationDomain) { WriteConcern = _settings.WriteConcern }; @@ -531,11 +531,11 @@ private ListDatabasesOperation CreateListDatabasesOperation(ListDatabasesOptions var messageEncoderSettings = GetMessageEncoderSettings(); var translationOptions = _settings.TranslationOptions; - return new ListDatabasesOperation(messageEncoderSettings) + return new ListDatabasesOperation(messageEncoderSettings, _settings.SerializationDomain) { AuthorizedDatabases = options.AuthorizedDatabases, Comment = options.Comment, - Filter = options.Filter?.Render(new(BsonDocumentSerializer.Instance, BsonSerializer.SerializerRegistry, translationOptions: translationOptions)), + Filter = options.Filter?.Render(new(BsonDocumentSerializer.Instance, (_settings as IInheritableMongoClientSettings).SerializationDomain, translationOptions: translationOptions)), NameOnly = options.NameOnly, RetryRequested = _settings.RetryReads }; @@ -563,7 +563,8 @@ private ChangeStreamOperation CreateChangeStreamOperation( _settings.ReadConcern, GetMessageEncoderSettings(), _settings.RetryReads, - _settings.TranslationOptions); + _settings.TranslationOptions, + _settings.SerializationDomain); private TResult ExecuteReadOperation(IClientSessionHandle session, IReadOperation operation, CancellationToken cancellationToken) => _operationExecutor.ExecuteReadOperation(session, operation, _readOperationOptions, false, cancellationToken); @@ -593,8 +594,7 @@ private MessageEncoderSettings GetMessageEncoderSettings() private RenderArgs GetRenderArgs() { var translationOptions = Settings.TranslationOptions; - var serializerRegistry = BsonSerializer.SerializerRegistry; - return new RenderArgs(BsonDocumentSerializer.Instance, serializerRegistry, translationOptions: translationOptions); + return new RenderArgs(BsonDocumentSerializer.Instance, (_settings as IInheritableMongoClientSettings).SerializationDomain, translationOptions: translationOptions); } private IClientSessionHandle StartSession(ClientSessionOptions options) diff --git a/src/MongoDB.Driver/MongoClientSettings.cs b/src/MongoDB.Driver/MongoClientSettings.cs index d7da362ca92..1c4446c3893 100644 --- a/src/MongoDB.Driver/MongoClientSettings.cs +++ b/src/MongoDB.Driver/MongoClientSettings.cs @@ -18,6 +18,7 @@ using System.Collections.ObjectModel; using System.Linq; using System.Text; +using MongoDB.Bson.Serialization; using MongoDB.Driver.Core.Compression; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Misc; @@ -66,6 +67,7 @@ public class MongoClientSettings : IEquatable, IInheritable private bool _retryReads; private bool _retryWrites; private ConnectionStringScheme _scheme; + private IBsonSerializationDomain _serializationDomain; private ServerApi _serverApi; private List _servers; private ServerMonitoringMode _serverMonitoringMode; @@ -117,6 +119,7 @@ public MongoClientSettings() _retryReads = true; _retryWrites = true; _scheme = ConnectionStringScheme.MongoDB; + _serializationDomain = BsonSerializer.DefaultSerializationDomain; _serverApi = null; _servers = new List { new MongoServerAddress("localhost") }; _serverMonitoringMode = ServerMonitoringMode.Auto; @@ -471,6 +474,21 @@ public ReadPreference ReadPreference } } + IBsonSerializationDomain IInheritableMongoClientSettings.SerializationDomain + { + //QUESTION Is this reasonable? + get => _serializationDomain ?? BsonSerializer.DefaultSerializationDomain; + set + { + if (_isFrozen) { throw new InvalidOperationException("MongoClientSettings is frozen."); } + _serializationDomain = value ?? throw new ArgumentNullException(nameof(value)); + } + } + + //FP This is a convenience property, it could be removed. + internal IBsonSerializationDomain SerializationDomain + => (this as IInheritableMongoClientSettings).SerializationDomain; + /// /// Gets or sets the name of the replica set. /// @@ -927,6 +945,7 @@ public MongoClientSettings Clone() clone._retryReads = _retryReads; clone._retryWrites = _retryWrites; clone._scheme = _scheme; + clone._serializationDomain = _serializationDomain; clone._serverApi = _serverApi; clone._servers = new List(_servers); clone._serverMonitoringMode = _serverMonitoringMode; diff --git a/src/MongoDB.Driver/MongoCollectionImpl.cs b/src/MongoDB.Driver/MongoCollectionImpl.cs index 8ad05198a18..2c69f46b788 100644 --- a/src/MongoDB.Driver/MongoCollectionImpl.cs +++ b/src/MongoDB.Driver/MongoCollectionImpl.cs @@ -682,7 +682,7 @@ private WriteRequest ConvertWriteModelToWriteRequest(WriteModel model var insertOneModel = (InsertOneModel)model; if (_settings.AssignIdOnInsert) { - _documentSerializer.SetDocumentIdIfMissing(this, insertOneModel.Document); + _documentSerializer.SetDocumentIdIfMissing(this, insertOneModel.Document, renderArgs.SerializationDomain); } return new InsertRequest(new BsonDocumentWrapper(insertOneModel.Document, _documentSerializer)) { @@ -760,7 +760,8 @@ private AggregateOperation CreateAggregateOperation(RenderedPi _collectionNamespace, renderedPipeline.Documents, renderedPipeline.OutputSerializer, - _messageEncoderSettings) + _messageEncoderSettings, + Settings.SerializationDomain) { AllowDiskUse = options.AllowDiskUse, BatchSize = options.BatchSize, @@ -782,7 +783,7 @@ private IAsyncCursor CreateAggregateToCollectionResultCursor(I { var outputCollectionNamespace = AggregateHelper.GetOutCollection(pipeline.Documents.Last(), _collectionNamespace.DatabaseNamespace); - var findOperation = new FindOperation(outputCollectionNamespace, pipeline.OutputSerializer, _messageEncoderSettings) + var findOperation = new FindOperation(outputCollectionNamespace, pipeline.OutputSerializer, _messageEncoderSettings, Settings.SerializationDomain) { BatchSize = options.BatchSize, Collation = options.Collation, @@ -807,7 +808,8 @@ private AggregateToCollectionOperation CreateAggregateToCollectionOperation CreateChangeStreamOperation( options, _settings.ReadConcern, messageEncoderSettings: _messageEncoderSettings, _database.Client.Settings.RetryReads, - translationOptions); + translationOptions, + _settings.SerializationDomain); } private CountDocumentsOperation CreateCountDocumentsOperation( @@ -874,7 +877,7 @@ private CountDocumentsOperation CreateCountDocumentsOperation( options ??= new CountOptions(); var renderArgs = GetRenderArgs(); - return new CountDocumentsOperation(_collectionNamespace, _messageEncoderSettings) + return new CountDocumentsOperation(_collectionNamespace, _messageEncoderSettings, Settings.SerializationDomain) { Collation = options.Collation, Comment = options.Comment, @@ -895,7 +898,7 @@ private CountOperation CreateCountOperation( options ??= new CountOptions(); var renderArgs = GetRenderArgs(); - return new CountOperation(_collectionNamespace, _messageEncoderSettings) + return new CountOperation(_collectionNamespace, _messageEncoderSettings, _settings.SerializationDomain) { Collation = options.Collation, Comment = options.Comment, @@ -923,7 +926,8 @@ private DistinctOperation CreateDistinctOperation( _collectionNamespace, valueSerializer, renderedField.FieldName, - _messageEncoderSettings) + _messageEncoderSettings, + _settings.SerializationDomain) { Collation = options.Collation, Comment = options.Comment, @@ -948,7 +952,8 @@ private DistinctOperation CreateDistinctManyOperation( _collectionNamespace, itemSerializer, renderedField.FieldName, - _messageEncoderSettings) + _messageEncoderSettings, + _settings.SerializationDomain) { Collation = options.Collation, Comment = options.Comment, @@ -961,7 +966,7 @@ private DistinctOperation CreateDistinctManyOperation( private EstimatedDocumentCountOperation CreateEstimatedDocumentCountOperation(EstimatedDocumentCountOptions options) { - return new EstimatedDocumentCountOperation(_collectionNamespace, _messageEncoderSettings) + return new EstimatedDocumentCountOperation(_collectionNamespace, _messageEncoderSettings, _settings.SerializationDomain) { Comment = options?.Comment, MaxTime = options?.MaxTime, @@ -982,7 +987,8 @@ private FindOneAndDeleteOperation CreateFindOneAndDeleteOperation(renderedProjection.ProjectionSerializer), - _messageEncoderSettings) + _messageEncoderSettings, + _settings.SerializationDomain) { Collation = options.Collation, Comment = options.Comment, @@ -1012,7 +1018,8 @@ private FindOneAndReplaceOperation CreateFindOneAndReplaceOperation filter.Render(renderArgs), new BsonDocumentWrapper(replacement, _documentSerializer), new FindAndModifyValueDeserializer(renderedProjection.ProjectionSerializer), - _messageEncoderSettings) + _messageEncoderSettings, + _settings.SerializationDomain) { BypassDocumentValidation = options.BypassDocumentValidation, Collation = options.Collation, @@ -1043,7 +1050,8 @@ private FindOneAndUpdateOperation CreateFindOneAndUpdateOperation(renderedProjection.ProjectionSerializer), - _messageEncoderSettings) + _messageEncoderSettings, + _settings.SerializationDomain) { ArrayFilters = RenderArrayFilters(options.ArrayFilters), BypassDocumentValidation = options.BypassDocumentValidation, @@ -1074,7 +1082,8 @@ private FindOperation CreateFindOperation( return new FindOperation( _collectionNamespace, renderedProjection.ProjectionSerializer, - _messageEncoderSettings) + _messageEncoderSettings, + Settings.SerializationDomain) { AllowDiskUse = options.AllowDiskUse, AllowPartialResults = options.AllowPartialResults, @@ -1118,7 +1127,8 @@ private MapReduceOperation CreateMapReduceOperation( map, reduce, resultSerializer, - _messageEncoderSettings) + _messageEncoderSettings, + Settings.SerializationDomain) { Collation = options.Collation, Filter = options.Filter?.Render(renderArgs), @@ -1155,7 +1165,8 @@ private MapReduceOutputToCollectionOperation CreateMapReduceOutputToCollectionOp outputCollectionNamespace, map, reduce, - _messageEncoderSettings) + _messageEncoderSettings, + _settings.SerializationDomain) { BypassDocumentValidation = options.BypassDocumentValidation, Collation = options.Collation, @@ -1187,7 +1198,8 @@ private IAsyncCursor CreateMapReduceOutputToCollectionResultCursor( outputCollectionNamespace, resultSerializer, - _messageEncoderSettings) + _messageEncoderSettings, + Settings.SerializationDomain) { Collation = options.Collation, MaxTime = options.MaxTime, @@ -1230,7 +1242,8 @@ private MessageEncoderSettings GetMessageEncoderSettings() var messageEncoderSettings = new MessageEncoderSettings { { MessageEncoderSettingsName.ReadEncoding, _settings.ReadEncoding ?? Utf8Encodings.Strict }, - { MessageEncoderSettingsName.WriteEncoding, _settings.WriteEncoding ?? Utf8Encodings.Strict } + { MessageEncoderSettingsName.WriteEncoding, _settings.WriteEncoding ?? Utf8Encodings.Strict }, + { MessageEncoderSettingsName.SerializationDomain, _settings.SerializationDomain } }; if (_database.Client is MongoClient mongoClient) @@ -1290,13 +1303,13 @@ private IBsonSerializer GetItemSerializerForDistinctMany(RenderedF private RenderArgs GetRenderArgs() { var translationOptions = _database.Client.Settings.TranslationOptions; - return new RenderArgs(_documentSerializer, _settings.SerializerRegistry, translationOptions: translationOptions); + return new RenderArgs(_documentSerializer, _settings.SerializationDomain, translationOptions: translationOptions); } private RenderArgs GetRenderArgs(ExpressionTranslationOptions translationOptions) { translationOptions = translationOptions.AddMissingOptionsFrom(_database.Client.Settings.TranslationOptions); - return new RenderArgs(_documentSerializer, _settings.SerializerRegistry, translationOptions: translationOptions); + return new RenderArgs(_documentSerializer, _settings.SerializationDomain, translationOptions: translationOptions); } private IEnumerable RenderArrayFilters(IEnumerable arrayFilters) @@ -1549,7 +1562,7 @@ private CreateIndexesOperation CreateCreateIndexesOperation(IEnumerable CreateCreateIndexRequests(IEnumerable CreateOneAsync(CreateSearchIndexModel model, Cancellat public void DropOne(string indexName, CancellationToken cancellationToken = default) { using var session = _collection._operationExecutor.StartImplicitSession(); - var operation = new DropSearchIndexOperation(_collection.CollectionNamespace, indexName, _collection._messageEncoderSettings); + var operation = new DropSearchIndexOperation(_collection.CollectionNamespace, indexName, _collection._messageEncoderSettings, _collection.Settings.SerializationDomain); _collection.ExecuteWriteOperation(session, operation, cancellationToken); } public async Task DropOneAsync(string indexName, CancellationToken cancellationToken = default) { using var session = _collection._operationExecutor.StartImplicitSession(); - var operation = new DropSearchIndexOperation(_collection.CollectionNamespace, indexName, _collection._messageEncoderSettings); + var operation = new DropSearchIndexOperation(_collection.CollectionNamespace, indexName, _collection._messageEncoderSettings, _collection.Settings.SerializationDomain); await _collection.ExecuteWriteOperationAsync(session, operation, cancellationToken).ConfigureAwait(false); } @@ -1699,14 +1712,14 @@ public Task> ListAsync(string indexName, AggregateOpt public void Update(string indexName, BsonDocument definition, CancellationToken cancellationToken = default) { using var session = _collection._operationExecutor.StartImplicitSession(); - var operation = new UpdateSearchIndexOperation(_collection.CollectionNamespace, indexName, definition, _collection._messageEncoderSettings); + var operation = new UpdateSearchIndexOperation(_collection.CollectionNamespace, indexName, definition, _collection._messageEncoderSettings, _collection.Settings.SerializationDomain); _collection.ExecuteWriteOperation(session, operation, cancellationToken); } public async Task UpdateAsync(string indexName, BsonDocument definition, CancellationToken cancellationToken = default) { using var session = _collection._operationExecutor.StartImplicitSession(); - var operation = new UpdateSearchIndexOperation(_collection.CollectionNamespace, indexName, definition, _collection._messageEncoderSettings); + var operation = new UpdateSearchIndexOperation(_collection.CollectionNamespace, indexName, definition, _collection._messageEncoderSettings, _collection.Settings.SerializationDomain); await _collection.ExecuteWriteOperationAsync(session, operation, cancellationToken).ConfigureAwait(false); } @@ -1721,7 +1734,8 @@ private PipelineDefinition CreateListIndexesStage(strin private CreateSearchIndexesOperation CreateCreateIndexesOperation(IEnumerable models) => new(_collection._collectionNamespace, models.Select(m => new CreateSearchIndexRequest(m.Name, m.Type, m.Definition)), - _collection._messageEncoderSettings); + _collection._messageEncoderSettings, + _collection.Settings.SerializationDomain); private string[] GetIndexNames(BsonDocument createSearchIndexesResponse) => createSearchIndexesResponse["indexesCreated"] diff --git a/src/MongoDB.Driver/MongoCollectionSettings.cs b/src/MongoDB.Driver/MongoCollectionSettings.cs index a8489008921..07320b9a29b 100644 --- a/src/MongoDB.Driver/MongoCollectionSettings.cs +++ b/src/MongoDB.Driver/MongoCollectionSettings.cs @@ -34,6 +34,7 @@ public class MongoCollectionSettings private Setting _readPreference; private Setting _writeConcern; private Setting _writeEncoding; + private Setting _serializationDomain; // the following fields are set when Freeze is called private bool _isFrozen; @@ -116,9 +117,24 @@ public ReadPreference ReadPreference /// /// Gets the serializer registry. /// - public IBsonSerializerRegistry SerializerRegistry + public IBsonSerializerRegistry SerializerRegistry => SerializationDomain.SerializerRegistry; + + /// + /// //TODO + /// + internal IBsonSerializationDomain SerializationDomain { - get { return BsonSerializer.SerializerRegistry; } + //QUESTION Is this reasonable? In normal uses cases the domain would be setup by ApplyDefaultValues, but for testing it would not work. + get => _serializationDomain.Value ?? BsonSerializer.DefaultSerializationDomain; + set + { + if (_isFrozen) { throw new InvalidOperationException("MongoCollectionSettings is frozen."); } + if (value == null) + { + throw new ArgumentNullException("value"); + } + _serializationDomain.Value = value; + } } /// @@ -165,6 +181,7 @@ public virtual MongoCollectionSettings Clone() clone._readPreference = _readPreference.Clone(); clone._writeConcern = _writeConcern.Clone(); clone._writeEncoding = _writeEncoding.Clone(); + clone._serializationDomain = _serializationDomain; //TODO .clone...? return clone; } @@ -298,6 +315,10 @@ internal void ApplyDefaultValues(MongoDatabaseSettings databaseSettings) { ReadPreference = databaseSettings.ReadPreference; } + if (!_serializationDomain.HasBeenSet) + { + SerializationDomain = databaseSettings.SerializationDomain; + } if (!_writeConcern.HasBeenSet) { WriteConcern = databaseSettings.WriteConcern; diff --git a/src/MongoDB.Driver/MongoDB.Driver.csproj b/src/MongoDB.Driver/MongoDB.Driver.csproj index 34add76dfbe..734e55b8df9 100644 --- a/src/MongoDB.Driver/MongoDB.Driver.csproj +++ b/src/MongoDB.Driver/MongoDB.Driver.csproj @@ -13,11 +13,12 @@ true - - - - - + + + + + + diff --git a/src/MongoDB.Driver/MongoDatabase.cs b/src/MongoDB.Driver/MongoDatabase.cs index ba942b62d70..1aea69f266a 100644 --- a/src/MongoDB.Driver/MongoDatabase.cs +++ b/src/MongoDB.Driver/MongoDatabase.cs @@ -525,7 +525,8 @@ private AggregateOperation CreateAggregateOperation(RenderedPi _databaseNamespace, renderedPipeline.Documents, renderedPipeline.OutputSerializer, - messageEncoderSettings) + messageEncoderSettings, + Settings.SerializationDomain) { AllowDiskUse = options.AllowDiskUse, BatchSize = options.BatchSize, @@ -552,7 +553,7 @@ private IAsyncCursor CreateAggregateToCollectionResultCursor(I // However, since we've added encryption configuration for CreateAggregateToCollectionOperation operation, // it's not superfluous to also add it here var messageEncoderSettings = GetMessageEncoderSettings(); - var findOperation = new FindOperation(outputCollectionNamespace, pipeline.OutputSerializer, messageEncoderSettings) + var findOperation = new FindOperation(outputCollectionNamespace, pipeline.OutputSerializer, messageEncoderSettings, Settings.SerializationDomain) { BatchSize = options.BatchSize, Collation = options.Collation, @@ -578,7 +579,8 @@ private AggregateToCollectionOperation CreateAggregateToCollectionOperation CreateCreateCollectionOperation var serializerRegistry = options.SerializerRegistry ?? BsonSerializer.SerializerRegistry; var documentSerializer = options.DocumentSerializer ?? serializerRegistry.GetSerializer(); - var clusteredIndex = options.ClusteredIndex?.Render(documentSerializer, serializerRegistry, translationOptions); - var validator = options.Validator?.Render(new(documentSerializer, serializerRegistry, translationOptions: translationOptions)); + //DOMAIN-API This will need to go away + //This is only happening in a couple of places, it's not worth to find better solutions right now. + RenderArgs renderArgs = options.SerializationDomain is null ? + new(documentSerializer, serializerRegistry, translationOptions: translationOptions) : + new(documentSerializer, options.SerializationDomain, translationOptions: translationOptions); + + //DOMAIN-API This will need to go away + var clusteredIndex = options.SerializationDomain is null? + options.ClusteredIndex?.Render(documentSerializer, serializerRegistry, translationOptions): + options.ClusteredIndex?.Render(documentSerializer, options.SerializationDomain, translationOptions); + var validator = options.Validator?.Render(renderArgs); var collectionNamespace = new CollectionNamespace(_databaseNamespace, name); var effectiveEncryptedFields = EncryptedCollectionHelper.GetEffectiveEncryptedFields(collectionNamespace, options.EncryptedFields, _client.Settings?.AutoEncryptionOptions?.EncryptedFieldsMap); @@ -624,6 +635,7 @@ private IWriteOperation CreateCreateCollectionOperation collectionNamespace, effectiveEncryptedFields, messageEncoderSettings, + _settings.SerializationDomain, createCollectionOperationConfigurator: cco => { cco.Capped = options.Capped; @@ -654,8 +666,14 @@ private CreateViewOperation CreateCreateViewOperation( var translationOptions = _client.Settings.TranslationOptions; var serializerRegistry = options.SerializerRegistry ?? BsonSerializer.SerializerRegistry; var documentSerializer = options.DocumentSerializer ?? serializerRegistry.GetSerializer(); - var pipelineDocuments = pipeline.Render(new (documentSerializer, serializerRegistry, translationOptions: translationOptions)).Documents; - return new CreateViewOperation(_databaseNamespace, viewName, viewOn, pipelineDocuments, GetMessageEncoderSettings()) + + //DOMAIN-API This will need to go away + RenderArgs renderArgs = options.SerializationDomain is null ? + new(documentSerializer, serializerRegistry, translationOptions: translationOptions) : + new(documentSerializer, options.SerializationDomain, translationOptions: translationOptions); + + var pipelineDocuments = pipeline.Render(renderArgs).Documents; + return new CreateViewOperation(_databaseNamespace, viewName, viewOn, pipelineDocuments, GetMessageEncoderSettings(), _settings.SerializationDomain) { Collation = options.Collation, WriteConcern = _settings.WriteConcern @@ -669,6 +687,7 @@ private IWriteOperation CreateDropCollectionOperation(CollectionNa collectionNamespace, effectiveEncryptedFields, messageEncoderSettings, + _settings.SerializationDomain, (dco) => { dco.WriteConcern = _settings.WriteConcern; @@ -679,7 +698,7 @@ private ListCollectionsOperation CreateListCollectionNamesOperation(ListCollecti { var messageEncoderSettings = GetMessageEncoderSettings(); var renderArgs = GetRenderArgs(BsonDocumentSerializer.Instance); - return new ListCollectionsOperation(_databaseNamespace, messageEncoderSettings) + return new ListCollectionsOperation(_databaseNamespace, messageEncoderSettings, Settings.SerializationDomain) { AuthorizedCollections = options?.AuthorizedCollections, Comment = options?.Comment, @@ -693,7 +712,7 @@ private ListCollectionsOperation CreateListCollectionsOperation(ListCollectionsO { var renderArgs = GetRenderArgs(BsonDocumentSerializer.Instance); var messageEncoderSettings = GetMessageEncoderSettings(); - return new ListCollectionsOperation(_databaseNamespace, messageEncoderSettings) + return new ListCollectionsOperation(_databaseNamespace, messageEncoderSettings, Settings.SerializationDomain) { BatchSize = options?.BatchSize, Comment = options?.Comment, @@ -710,7 +729,8 @@ private RenameCollectionOperation CreateRenameCollectionOperation(string oldName return new RenameCollectionOperation( new CollectionNamespace(_databaseNamespace, oldName), new CollectionNamespace(_databaseNamespace, newName), - messageEncoderSettings) + messageEncoderSettings, + _settings.SerializationDomain) { DropTarget = options.DropTarget, WriteConcern = _settings.WriteConcern @@ -721,7 +741,7 @@ private ReadCommandOperation CreateRunCommandOperation(Command { var renderedCommand = command.Render(_settings.SerializerRegistry); var messageEncoderSettings = GetMessageEncoderSettings(); - return new ReadCommandOperation(_databaseNamespace, renderedCommand.Document, renderedCommand.ResultSerializer, messageEncoderSettings) + return new ReadCommandOperation(_databaseNamespace, renderedCommand.Document, renderedCommand.ResultSerializer, messageEncoderSettings, Settings.SerializationDomain) { RetryRequested = false }; @@ -740,7 +760,8 @@ private ChangeStreamOperation CreateChangeStreamOperation( _settings.ReadConcern, GetMessageEncoderSettings(), _client.Settings.RetryReads, - translationOptions); + translationOptions, + _client.Settings.SerializationDomain); } private TResult ExecuteReadOperation(IClientSessionHandle session, IReadOperation operation, CancellationToken cancellationToken) @@ -812,7 +833,8 @@ private MessageEncoderSettings GetMessageEncoderSettings() var messageEncoderSettings = new MessageEncoderSettings { { MessageEncoderSettingsName.ReadEncoding, _settings.ReadEncoding ?? Utf8Encodings.Strict }, - { MessageEncoderSettingsName.WriteEncoding, _settings.WriteEncoding ?? Utf8Encodings.Strict } + { MessageEncoderSettingsName.WriteEncoding, _settings.WriteEncoding ?? Utf8Encodings.Strict }, + { MessageEncoderSettingsName.SerializationDomain, _settings.SerializationDomain } }; if (_client is MongoClient mongoClient) @@ -826,13 +848,13 @@ private MessageEncoderSettings GetMessageEncoderSettings() private RenderArgs GetRenderArgs(IBsonSerializer documentSerializer) { var translationOptions = _client.Settings.TranslationOptions; - return new RenderArgs(documentSerializer, _settings.SerializerRegistry, translationOptions: translationOptions); + return new RenderArgs(documentSerializer, _settings.SerializationDomain, translationOptions: translationOptions); } private RenderArgs GetRenderArgs(IBsonSerializer documentSerializer, ExpressionTranslationOptions translationOptions) { translationOptions = translationOptions.AddMissingOptionsFrom(_client.Settings.TranslationOptions); - return new RenderArgs(documentSerializer, _settings.SerializerRegistry, translationOptions: translationOptions); + return new RenderArgs(documentSerializer, _settings.SerializationDomain, translationOptions: translationOptions); } } } diff --git a/src/MongoDB.Driver/MongoDatabaseSettings.cs b/src/MongoDB.Driver/MongoDatabaseSettings.cs index b82ea52828d..8adeecdfcc4 100644 --- a/src/MongoDB.Driver/MongoDatabaseSettings.cs +++ b/src/MongoDB.Driver/MongoDatabaseSettings.cs @@ -33,6 +33,7 @@ public class MongoDatabaseSettings private Setting _readPreference; private Setting _writeConcern; private Setting _writeEncoding; + private Setting _serializationDomain; // the following fields are set when Freeze is called private bool _isFrozen; @@ -101,9 +102,23 @@ public ReadPreference ReadPreference /// /// Gets the serializer registry. /// - public IBsonSerializerRegistry SerializerRegistry + public IBsonSerializerRegistry SerializerRegistry => SerializationDomain.SerializerRegistry; + + /// + /// //TODO + /// + internal IBsonSerializationDomain SerializationDomain { - get { return BsonSerializer.SerializerRegistry; } + get => _serializationDomain.Value ?? BsonSerializer.DefaultSerializationDomain; //QUESTION Is this reasonable? + set + { + if (_isFrozen) { throw new InvalidOperationException("MongoCollectionSettings is frozen."); } + if (value == null) + { + throw new ArgumentNullException("value"); + } + _serializationDomain.Value = value; + } } /// @@ -149,6 +164,7 @@ public MongoDatabaseSettings Clone() clone._readPreference = _readPreference.Clone(); clone._writeConcern = _writeConcern.Clone(); clone._writeEncoding = _writeEncoding.Clone(); + clone._serializationDomain = _serializationDomain; return clone; } @@ -275,6 +291,10 @@ internal void ApplyDefaultValues(IInheritableMongoClientSettings clientSettings) { ReadPreference = clientSettings.ReadPreference; } + if (!_serializationDomain.HasBeenSet) + { + SerializationDomain = clientSettings.SerializationDomain; + } if (!_writeConcern.HasBeenSet) { WriteConcern = clientSettings.WriteConcern; diff --git a/src/MongoDB.Driver/OfTypeMongoCollection.cs b/src/MongoDB.Driver/OfTypeMongoCollection.cs index a1b1dd69220..43a7d27cad2 100644 --- a/src/MongoDB.Driver/OfTypeMongoCollection.cs +++ b/src/MongoDB.Driver/OfTypeMongoCollection.cs @@ -61,9 +61,9 @@ protected override UpdateDefinition AdjustUpdateDefinition(Upd if (isUpsert) { - var discriminatorConvention = _rootDocumentCollection.DocumentSerializer.GetDiscriminatorConvention(); + var discriminatorConvention = _rootDocumentCollection.DocumentSerializer.GetDiscriminatorConvention(Settings.SerializationDomain); var discriminatorConventionElementName = discriminatorConvention.ElementName; - var discriminator = discriminatorConvention.GetDiscriminator(typeof(TRootDocument), typeof(TDerivedDocument)); + var discriminator = discriminatorConvention.GetDiscriminatorInternal(typeof(TRootDocument), typeof(TDerivedDocument), Settings.SerializationDomain); if (result is PipelineUpdateDefinition pipeline) { diff --git a/src/MongoDB.Driver/OfTypeSerializer.cs b/src/MongoDB.Driver/OfTypeSerializer.cs index 1ff1b95e4c2..84b07c4813a 100644 --- a/src/MongoDB.Driver/OfTypeSerializer.cs +++ b/src/MongoDB.Driver/OfTypeSerializer.cs @@ -14,12 +14,13 @@ */ using System; +using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; namespace MongoDB.Driver { - internal sealed class OfTypeSerializer : SerializerBase, IBsonDocumentSerializer, IBsonIdProvider + internal sealed class OfTypeSerializer : SerializerBase, IBsonDocumentSerializer, IBsonIdProviderInternal where TDerivedDocument : TRootDocument { private readonly IBsonSerializer _derivedDocumentSerializer; @@ -46,10 +47,14 @@ obj is OfTypeSerializer other && } public bool GetDocumentId(object document, out object id, out Type idNominalType, out IIdGenerator idGenerator) + => GetDocumentId(document, BsonSerializer.DefaultSerializationDomain, out id, out idNominalType, out idGenerator); + + public bool GetDocumentId(object document, IBsonSerializationDomain serializationDomain, out object id, out Type idNominalType, + out IIdGenerator idGenerator) { if (_derivedDocumentSerializer is IBsonIdProvider idProvider) { - return idProvider.GetDocumentId(document, out id, out idNominalType, out idGenerator); + return idProvider.GetDocumentIdInternal(document, serializationDomain, out id, out idNominalType, out idGenerator); } else { diff --git a/src/MongoDB.Driver/PipelineDefinition.cs b/src/MongoDB.Driver/PipelineDefinition.cs index fbd9d69d5c0..5b346c395eb 100644 --- a/src/MongoDB.Driver/PipelineDefinition.cs +++ b/src/MongoDB.Driver/PipelineDefinition.cs @@ -21,6 +21,7 @@ using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Linq; +using MongoDB.Driver.Support; namespace MongoDB.Driver { @@ -329,7 +330,7 @@ public override RenderedPipelineDefinition Render(RenderArgs ar IBsonSerializer currentSerializer = args.DocumentSerializer; foreach (var stage in _stages) { - var renderedStage = stage.Render(currentSerializer, args.SerializerRegistry, args.TranslationOptions); + var renderedStage = stage.RenderInternal(currentSerializer, args.SerializationDomain, args.SerializerRegistry, args.TranslationOptions); currentSerializer = renderedStage.OutputSerializer; foreach (var document in renderedStage.Documents) { diff --git a/src/MongoDB.Driver/PipelineDefinitionBuilder.cs b/src/MongoDB.Driver/PipelineDefinitionBuilder.cs index 3ad6eca971a..1b3de33a4ae 100644 --- a/src/MongoDB.Driver/PipelineDefinitionBuilder.cs +++ b/src/MongoDB.Driver/PipelineDefinitionBuilder.cs @@ -1644,7 +1644,7 @@ public AppendedStagePipelineDefinition( public override RenderedPipelineDefinition Render(RenderArgs args) { var renderedPipeline = _pipeline.Render(args); - var renderedStage = _stage.Render(new(renderedPipeline.OutputSerializer, args.SerializerRegistry, translationOptions: args.TranslationOptions)); + var renderedStage = _stage.Render(new(renderedPipeline.OutputSerializer, args.SerializationDomain, translationOptions: args.TranslationOptions)); var documents = renderedPipeline.Documents.Concat(renderedStage.Documents); var outputSerializer = _outputSerializer ?? renderedStage.OutputSerializer; return new RenderedPipelineDefinition(documents, outputSerializer); @@ -1720,7 +1720,7 @@ public PrependedStagePipelineDefinition( public override RenderedPipelineDefinition Render(RenderArgs args) { var renderedStage = _stage.Render(args); - var renderedPipeline = _pipeline.Render(new(renderedStage.OutputSerializer, args.SerializerRegistry, translationOptions: args.TranslationOptions)); + var renderedPipeline = _pipeline.Render(new(renderedStage.OutputSerializer, args.SerializationDomain, translationOptions: args.TranslationOptions)); var documents = renderedStage.Documents.Concat(renderedPipeline.Documents); var outputSerializer = _outputSerializer ?? renderedPipeline.OutputSerializer; return new RenderedPipelineDefinition(documents, outputSerializer); diff --git a/src/MongoDB.Driver/PipelineStageDefinition.cs b/src/MongoDB.Driver/PipelineStageDefinition.cs index 27f1dcaae52..900769862b6 100644 --- a/src/MongoDB.Driver/PipelineStageDefinition.cs +++ b/src/MongoDB.Driver/PipelineStageDefinition.cs @@ -16,7 +16,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Transactions; using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Driver.Core.Misc; @@ -139,6 +138,7 @@ public interface IPipelineStageDefinition /// Type OutputType { get; } + //DOMAIN-API We need to swap the serializer registry for the serialization domain. /// /// Renders the specified document serializer. /// @@ -167,12 +167,17 @@ public interface IPipelineStageDefinition string ToString(IBsonSerializer inputSerializer, IBsonSerializerRegistry serializerRegistry); } + internal interface IPipelineStageStageDefinitionInternal : IPipelineStageDefinition + { + IRenderedPipelineStageDefinition Render(IBsonSerializer inputSerializer, IBsonSerializationDomain serializationDomain, ExpressionTranslationOptions translationOptions); + } + /// /// Base class for pipeline stages. /// /// The type of the input. /// The type of the output. - public abstract class PipelineStageDefinition : IPipelineStageDefinition + public abstract class PipelineStageDefinition : IPipelineStageStageDefinitionInternal { /// /// Gets the type of the input. @@ -292,6 +297,12 @@ IRenderedPipelineStageDefinition IPipelineStageDefinition.Render(IBsonSerializer { return Render(new((IBsonSerializer)inputSerializer, serializerRegistry, translationOptions: translationOptions)); } + + IRenderedPipelineStageDefinition IPipelineStageStageDefinitionInternal.Render(IBsonSerializer inputSerializer, IBsonSerializationDomain serializationDomain, + ExpressionTranslationOptions translationOptions) + { + return Render(new((IBsonSerializer)inputSerializer, serializationDomain, translationOptions: translationOptions)); + } } /// diff --git a/src/MongoDB.Driver/PipelineStageDefinitionBuilder.cs b/src/MongoDB.Driver/PipelineStageDefinitionBuilder.cs index b8c7ee834fb..8cc047a228e 100644 --- a/src/MongoDB.Driver/PipelineStageDefinitionBuilder.cs +++ b/src/MongoDB.Driver/PipelineStageDefinitionBuilder.cs @@ -544,6 +544,7 @@ public static PipelineStageDefinition Facet( const string operatorName = "$facet"; var materializedFacets = facets.ToArray(); + var stage = new DelegatedPipelineStageDefinition( operatorName, args => @@ -556,7 +557,25 @@ public static PipelineStageDefinition Facet( facetsDocument.Add(facet.Name, renderedPipeline); } var document = new BsonDocument("$facet", facetsDocument); - var outputSerializer = options?.OutputSerializer ?? args.SerializerRegistry.GetSerializer(); + + IBsonSerializer outputSerializer; + + if (options?.OutputSerializer is not null) + { + outputSerializer = options.OutputSerializer; + } + else if (typeof(TOutput) == typeof(AggregateFacetResults)) + { + outputSerializer = (IBsonSerializer)new AggregateFacetResultsSerializer( + materializedFacets.Select(f => f.Name), + materializedFacets.Select(f => f.OutputSerializer ?? args.SerializerRegistry.GetSerializer(f.OutputType))); //QUESTION What do we do? Do we delay the setting of the serializer..? + } + else + { + outputSerializer = args.SerializerRegistry.GetSerializer(); + } + + //var outputSerializer = options?.OutputSerializer ?? args.SerializerRegistry.GetSerializer(); return new RenderedPipelineStageDefinition(operatorName, document, outputSerializer); }); @@ -572,12 +591,7 @@ public static PipelineStageDefinition Facet( public static PipelineStageDefinition Facet( IEnumerable> facets) { - Ensure.IsNotNull(facets, nameof(facets)); - var outputSerializer = new AggregateFacetResultsSerializer( - facets.Select(f => f.Name), - facets.Select(f => f.OutputSerializer ?? BsonSerializer.SerializerRegistry.GetSerializer(f.OutputType))); - var options = new AggregateFacetOptions { OutputSerializer = outputSerializer }; - return Facet(facets, options); + return Facet(facets, options: null); } /// @@ -1280,7 +1294,7 @@ public static PipelineStageDefinition OfType( else { var inputSerializer = args.DocumentSerializer; - var discriminatorConvention = inputSerializer.GetDiscriminatorConvention(); + var discriminatorConvention = inputSerializer.GetDiscriminatorConvention(args.SerializationDomain); if (discriminatorConvention == null) { var message = string.Format("OfType requires that a discriminator convention exist for type: {0}.", BsonUtils.GetFriendlyTypeName(typeof(TOutput))); @@ -1290,8 +1304,8 @@ public static PipelineStageDefinition OfType( var discriminatorField = new AstFilterField(discriminatorConvention.ElementName); ofTypeFilter = discriminatorConvention switch { - IHierarchicalDiscriminatorConvention hierarchicalDiscriminatorConvention => DiscriminatorAstFilter.TypeIs(discriminatorField, hierarchicalDiscriminatorConvention, nominalType, actualType), - IScalarDiscriminatorConvention scalarDiscriminatorConvention => DiscriminatorAstFilter.TypeIs(discriminatorField, scalarDiscriminatorConvention, nominalType, actualType), + IHierarchicalDiscriminatorConvention hierarchicalDiscriminatorConvention => DiscriminatorAstFilter.TypeIs(discriminatorField, hierarchicalDiscriminatorConvention, nominalType, actualType, args.SerializationDomain), + IScalarDiscriminatorConvention scalarDiscriminatorConvention => DiscriminatorAstFilter.TypeIs(discriminatorField, scalarDiscriminatorConvention, nominalType, actualType, args.SerializationDomain), _ => throw new NotSupportedException( "OfType is not supported with the configured discriminator convention.") }; } @@ -2224,8 +2238,8 @@ public Expression> Expression } public override RenderedProjectionDefinition Render(RenderArgs args) => args.RenderForFind ? - LinqProviderAdapter.TranslateExpressionToFindProjection(_expression, args.DocumentSerializer, args.SerializerRegistry, args.TranslationOptions) : - LinqProviderAdapter.TranslateExpressionToProjection(_expression, args.DocumentSerializer, args.SerializerRegistry, args.TranslationOptions); + LinqProviderAdapter.TranslateExpressionToFindProjection(_expression, args.DocumentSerializer, args.SerializationDomain, args.TranslationOptions) : + LinqProviderAdapter.TranslateExpressionToProjection(_expression, args.DocumentSerializer, args.SerializationDomain, args.TranslationOptions); } internal class SortPipelineStageDefinition : PipelineStageDefinition diff --git a/src/MongoDB.Driver/ProjectionDefinition.cs b/src/MongoDB.Driver/ProjectionDefinition.cs index 6ae7985cf5b..99c58af8107 100644 --- a/src/MongoDB.Driver/ProjectionDefinition.cs +++ b/src/MongoDB.Driver/ProjectionDefinition.cs @@ -277,11 +277,11 @@ public override RenderedProjectionDefinition Render(RenderArgs private readonly bool _renderForFind = false; private readonly IBsonSerializerRegistry _serializerRegistry = default; private readonly ExpressionTranslationOptions _translationOptions = default; + private readonly IBsonSerializationDomain _serializationDomain = default; + //DOMAIN-API We need to stop using this constructor, and use the one with the serialization domain instead. /// /// Initializes a new instance of the record. /// @@ -65,6 +67,36 @@ public RenderArgs( _renderForFind = renderForFind; _renderForElemMatch = renderForElemMatch; _translationOptions = translationOptions; // can be null + _serializationDomain = BsonSerializer.DefaultSerializationDomain; + } + + /// + /// Initializes a new instance of the record. + /// + /// The document serializer. + /// The path render arguments. + /// Value that specifies whether full dollar for should be rendered. + /// Value that specifies whether rendering a find operation. + /// Value that specifies whether rendering an $elemMatch. + /// The translation options. + /// //TODO + internal RenderArgs( + IBsonSerializer documentSerializer, + IBsonSerializationDomain serializationDomain, + PathRenderArgs pathRenderArgs = default, + bool renderDollarForm = default, + bool renderForFind = false, + bool renderForElemMatch = false, + ExpressionTranslationOptions translationOptions = null) + { + DocumentSerializer = documentSerializer; + PathRenderArgs = pathRenderArgs; + RenderDollarForm = renderDollarForm; + _serializationDomain = serializationDomain ?? BsonSerializer.DefaultSerializationDomain; + _renderForFind = renderForFind; + _renderForElemMatch = renderForElemMatch; + _translationOptions = translationOptions; // can be null + SerializerRegistry = _serializationDomain.SerializerRegistry; } /// @@ -99,12 +131,18 @@ public readonly IBsonSerializer DocumentSerializer /// /// Gets the serializer registry. /// - public readonly IBsonSerializerRegistry SerializerRegistry + public readonly IBsonSerializerRegistry SerializerRegistry //TODO: we should probably remove this property { get => _serializerRegistry; init => _serializerRegistry = Ensure.IsNotNull(value, nameof(value)); } + internal readonly IBsonSerializationDomain SerializationDomain + { + get => _serializationDomain; + init => _serializationDomain = Ensure.IsNotNull(value, nameof(value)); + } + /// /// Gets the translation options used when translation Expressions to MQL. /// @@ -129,6 +167,6 @@ public readonly IBsonSerializer GetSerializer() => /// A new RenderArgs{TNewDocument} instance. /// public readonly RenderArgs WithNewDocumentType(IBsonSerializer serializer) => - new(serializer, _serializerRegistry, _pathRenderArgs, _renderDollarForm, _renderForFind, _renderForElemMatch, _translationOptions); + new(serializer, _serializationDomain, _pathRenderArgs, _renderDollarForm, _renderForFind, _renderForElemMatch, _translationOptions); } } diff --git a/src/MongoDB.Driver/SetFieldDefinitions.cs b/src/MongoDB.Driver/SetFieldDefinitions.cs index 76dcc1c1914..f660419bec3 100644 --- a/src/MongoDB.Driver/SetFieldDefinitions.cs +++ b/src/MongoDB.Driver/SetFieldDefinitions.cs @@ -93,7 +93,7 @@ public ExpressionSetFieldDefinitions(Expression> expres /// public override BsonDocument Render(RenderArgs args) { - var stage = LinqProviderAdapter.TranslateExpressionToSetStage(_expression, args.DocumentSerializer, args.SerializerRegistry, args.TranslationOptions); + var stage = LinqProviderAdapter.TranslateExpressionToSetStage(_expression, args.DocumentSerializer, args.SerializationDomain, args.TranslationOptions); return stage["$set"].AsBsonDocument; } } diff --git a/src/MongoDB.Driver/Support/InternalExtensions.cs b/src/MongoDB.Driver/Support/InternalExtensions.cs new file mode 100644 index 00000000000..8ffefef2d01 --- /dev/null +++ b/src/MongoDB.Driver/Support/InternalExtensions.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.Driver.Support +{ + internal static class InternalExtensions + { + #region IPipelineStageDefinition + + public static IRenderedPipelineStageDefinition RenderInternal(this IPipelineStageDefinition pipelineStageDefinition, IBsonSerializer inputSerializer, + IBsonSerializationDomain serializationDomain, IBsonSerializerRegistry serializerRegistry, ExpressionTranslationOptions translationOptions) + { + if (pipelineStageDefinition is IPipelineStageStageDefinitionInternal pipelineStageStageDefinitionInternal) + { + return pipelineStageStageDefinitionInternal + .Render(inputSerializer, serializationDomain, translationOptions); + } + + return pipelineStageDefinition + .Render(inputSerializer, serializerRegistry, translationOptions); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/MongoDB.Driver/Support/ReflectionExtensions.cs b/src/MongoDB.Driver/Support/ReflectionExtensions.cs index 7bae4fa777e..b078332fa20 100644 --- a/src/MongoDB.Driver/Support/ReflectionExtensions.cs +++ b/src/MongoDB.Driver/Support/ReflectionExtensions.cs @@ -18,6 +18,7 @@ using System.Linq; using System.Reflection; using MongoDB.Bson; +using MongoDB.Bson.Serialization; namespace MongoDB.Driver.Support { @@ -62,20 +63,21 @@ public static bool IsBooleanOrNullableBoolean(this Type type) } } - public static bool IsNullable(this Type type) - { - return type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); - } - - public static bool IsNullableEnum(this Type type) - { - if (!IsNullable(type)) - { - return false; - } - - return GetNullableUnderlyingType(type).GetTypeInfo().IsEnum; - } + // Those have been commented out because there are identical methods in Bson assembly. + // public static bool IsNullable(this Type type) + // { + // return type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + // } + // + // public static bool IsNullableEnum(this Type type) + // { + // if (!IsNullable(type)) + // { + // return false; + // } + // + // return GetNullableUnderlyingType(type).GetTypeInfo().IsEnum; + // } public static bool IsNumeric(this Type type) { @@ -120,7 +122,7 @@ public static bool IsConvertibleToEnum(this Type type) public static Type GetNullableUnderlyingType(this Type type) { - if (!IsNullable(type)) + if (!type.IsNullable()) { throw new ArgumentException("Type must be nullable.", "type"); } diff --git a/src/MongoDB.Driver/UpdateDefinition.cs b/src/MongoDB.Driver/UpdateDefinition.cs index e7434a14d0d..45fbe905edf 100644 --- a/src/MongoDB.Driver/UpdateDefinition.cs +++ b/src/MongoDB.Driver/UpdateDefinition.cs @@ -120,6 +120,7 @@ public override string ToString() return ToString(inputSerializer, serializerRegistry); } + //DOMAIN-API These versions will need to go away. /// /// Returns a that represents this instance. /// diff --git a/src/MongoDB.Driver/UpdateDefinitionBuilder.cs b/src/MongoDB.Driver/UpdateDefinitionBuilder.cs index be95f9a306c..17f9f4393dd 100644 --- a/src/MongoDB.Driver/UpdateDefinitionBuilder.cs +++ b/src/MongoDB.Driver/UpdateDefinitionBuilder.cs @@ -1366,7 +1366,7 @@ public override BsonValue Render(RenderArgs args) var document = new BsonDocument(); using (var bsonWriter = new BsonDocumentWriter(document)) { - var context = BsonSerializationContext.CreateRoot(bsonWriter); + var context = BsonSerializationContext.CreateRoot(bsonWriter, args.SerializationDomain); bsonWriter.WriteStartDocument(); bsonWriter.WriteName("$addToSet"); bsonWriter.WriteStartDocument(); @@ -1460,7 +1460,7 @@ public override BsonValue Render(RenderArgs args) var document = new BsonDocument(); using (var bsonWriter = new BsonDocumentWriter(document)) { - var context = BsonSerializationContext.CreateRoot(bsonWriter); + var context = BsonSerializationContext.CreateRoot(bsonWriter, args.SerializationDomain); bsonWriter.WriteStartDocument(); bsonWriter.WriteName("$bit"); bsonWriter.WriteStartDocument(); @@ -1517,7 +1517,7 @@ public override BsonValue Render(RenderArgs args) var document = new BsonDocument(); using (var bsonWriter = new BsonDocumentWriter(document)) { - var context = BsonSerializationContext.CreateRoot(bsonWriter); + var context = BsonSerializationContext.CreateRoot(bsonWriter, args.SerializationDomain); bsonWriter.WriteStartDocument(); bsonWriter.WriteName(_operatorName); bsonWriter.WriteStartDocument(); @@ -1581,7 +1581,7 @@ public override BsonValue Render(RenderArgs args) var document = new BsonDocument(); using (var bsonWriter = new BsonDocumentWriter(document)) { - var context = BsonSerializationContext.CreateRoot(bsonWriter); + var context = BsonSerializationContext.CreateRoot(bsonWriter, args.SerializationDomain); bsonWriter.WriteStartDocument(); bsonWriter.WriteName(_values.Count == 1 ? "$pull" : "$pullAll"); bsonWriter.WriteStartDocument(); @@ -1648,7 +1648,7 @@ public override BsonValue Render(RenderArgs args) var document = new BsonDocument(); using (var bsonWriter = new BsonDocumentWriter(document)) { - var context = BsonSerializationContext.CreateRoot(bsonWriter); + var context = BsonSerializationContext.CreateRoot(bsonWriter, args.SerializationDomain); bsonWriter.WriteStartDocument(); bsonWriter.WriteName("$push"); bsonWriter.WriteStartDocument(); diff --git a/tests/MongoDB.Bson.Tests/ObjectModel/BsonDocumentWrapperTests.cs b/tests/MongoDB.Bson.Tests/ObjectModel/BsonDocumentWrapperTests.cs index a1f94fb3b41..12dc65583ec 100644 --- a/tests/MongoDB.Bson.Tests/ObjectModel/BsonDocumentWrapperTests.cs +++ b/tests/MongoDB.Bson.Tests/ObjectModel/BsonDocumentWrapperTests.cs @@ -305,12 +305,12 @@ public void TestCreateWithNominalTypeAndObject() { var c = CreateC(); - var wrapper = BsonDocumentWrapper.Create(typeof(C), c); + var wrapper = BsonDocumentWrapper.Create(typeof(C), value: c); Assert.Same(BsonSerializer.LookupSerializer(typeof(C)), wrapper.Serializer); Assert.Same(c, wrapper.Wrapped); Assert.Equal(false, wrapper.IsMaterialized); - wrapper = BsonDocumentWrapper.Create(typeof(C), null); + wrapper = BsonDocumentWrapper.Create(typeof(C), value: null); Assert.Same(BsonSerializer.LookupSerializer(typeof(C)), wrapper.Serializer); Assert.Same(null, wrapper.Wrapped); Assert.Equal(false, wrapper.IsMaterialized); diff --git a/tests/MongoDB.Driver.TestHelpers/Core/CoreTestConfiguration.cs b/tests/MongoDB.Driver.TestHelpers/Core/CoreTestConfiguration.cs index 8a2984ff2c4..fa1fb891371 100644 --- a/tests/MongoDB.Driver.TestHelpers/Core/CoreTestConfiguration.cs +++ b/tests/MongoDB.Driver.TestHelpers/Core/CoreTestConfiguration.cs @@ -20,6 +20,7 @@ using System.Security.Cryptography.X509Certificates; using System.Threading; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Authentication; using MongoDB.Driver.Core.Bindings; @@ -313,7 +314,7 @@ private static int GetMaxWireVersion() using (var binding = CreateReadBinding(session)) { var command = new BsonDocument("hello", 1); - var operation = new ReadCommandOperation(DatabaseNamespace.Admin, command, BsonDocumentSerializer.Instance, __messageEncoderSettings); + var operation = new ReadCommandOperation(DatabaseNamespace.Admin, command, BsonDocumentSerializer.Instance, __messageEncoderSettings, BsonSerializer.DefaultSerializationDomain); var response = operation.Execute(OperationContext.NoTimeout, binding); return response["maxWireVersion"].AsInt32; } @@ -325,7 +326,7 @@ private static SemanticVersion GetServerVersion() using (var binding = CreateReadBinding(session)) { var command = new BsonDocument("buildinfo", 1); - var operation = new ReadCommandOperation(DatabaseNamespace.Admin, command, BsonDocumentSerializer.Instance, __messageEncoderSettings); + var operation = new ReadCommandOperation(DatabaseNamespace.Admin, command, BsonDocumentSerializer.Instance, __messageEncoderSettings, BsonSerializer.DefaultSerializationDomain); var response = operation.Execute(OperationContext.NoTimeout, binding); return SemanticVersion.Parse(response["version"].AsString); } @@ -337,7 +338,7 @@ public static BsonDocument GetServerParameters() using (var binding = CreateReadBinding(session)) { var command = new BsonDocument("getParameter", new BsonString("*")); - var operation = new ReadCommandOperation(DatabaseNamespace.Admin, command, BsonDocumentSerializer.Instance, __messageEncoderSettings); + var operation = new ReadCommandOperation(DatabaseNamespace.Admin, command, BsonDocumentSerializer.Instance, __messageEncoderSettings, BsonSerializer.DefaultSerializationDomain); var serverParameters = operation.Execute(OperationContext.NoTimeout, binding); return serverParameters; @@ -399,7 +400,7 @@ private static IReadWriteBindingHandle CreateReadWriteBinding(ICoreSessionHandle private static void DropDatabase() { - var operation = new DropDatabaseOperation(__databaseNamespace.Value, __messageEncoderSettings); + var operation = new DropDatabaseOperation(__databaseNamespace.Value, __messageEncoderSettings, BsonSerializer.DefaultSerializationDomain); using (var session = StartSession()) using (var binding = CreateReadWriteBinding(session)) @@ -413,7 +414,7 @@ private static IEnumerable FindDocuments(IClusterInternal cluster, using (var session = StartSession(cluster)) using (var binding = CreateReadBinding(cluster, ReadPreference.Primary, session)) { - var operation = new FindOperation(collectionNamespace, BsonDocumentSerializer.Instance, __messageEncoderSettings); + var operation = new FindOperation(collectionNamespace, BsonDocumentSerializer.Instance, __messageEncoderSettings, BsonSerializer.DefaultSerializationDomain); return operation.Execute(OperationContext.NoTimeout, binding).ToList(); } @@ -493,7 +494,7 @@ string GetStorageEngineForCluster(IClusterInternal cluster) using (var session = StartSession(cluster)) using (var binding = CreateReadBinding(cluster, ReadPreference.PrimaryPreferred, session)) { - var operation = new ReadCommandOperation(DatabaseNamespace.Admin, command, BsonDocumentSerializer.Instance, __messageEncoderSettings); + var operation = new ReadCommandOperation(DatabaseNamespace.Admin, command, BsonDocumentSerializer.Instance, __messageEncoderSettings, BsonSerializer.DefaultSerializationDomain); var response = operation.Execute(OperationContext.NoTimeout, binding); if (response.TryGetValue("storageEngine", out var storageEngine) && storageEngine.AsBsonDocument.TryGetValue("name", out var name)) diff --git a/tests/MongoDB.Driver.TestHelpers/Core/FailPoint.cs b/tests/MongoDB.Driver.TestHelpers/Core/FailPoint.cs index 0122eb8447b..bc575e475ea 100644 --- a/tests/MongoDB.Driver.TestHelpers/Core/FailPoint.cs +++ b/tests/MongoDB.Driver.TestHelpers/Core/FailPoint.cs @@ -16,6 +16,7 @@ using System; using System.Threading; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; @@ -184,7 +185,8 @@ private void ExecuteCommand(BsonDocument command, bool waitForConnected) adminDatabase, command, BsonDocumentSerializer.Instance, - new MessageEncoderSettings()); + new MessageEncoderSettings(), + BsonSerializer.DefaultSerializationDomain); operation.Execute(OperationContext.NoTimeout, _binding); } diff --git a/tests/MongoDB.Driver.Tests/ChangeStreamHelperTests.cs b/tests/MongoDB.Driver.Tests/ChangeStreamHelperTests.cs index 450c92f8ef4..dcd5ba60f9d 100644 --- a/tests/MongoDB.Driver.Tests/ChangeStreamHelperTests.cs +++ b/tests/MongoDB.Driver.Tests/ChangeStreamHelperTests.cs @@ -45,7 +45,7 @@ public void CreateChangeStreamOperation_for_client_returns_expected_result() var messageEncoderSettings = new MessageEncoderSettings(); var renderedPipeline = RenderPipeline(pipeline); - var result = ChangeStreamHelper.CreateChangeStreamOperation(pipeline, options, readConcern, messageEncoderSettings, retryRequested: true, translationOptions: null); + var result = ChangeStreamHelper.CreateChangeStreamOperation(pipeline, options, readConcern, messageEncoderSettings, retryRequested: true, translationOptions: null, BsonSerializer.DefaultSerializationDomain); result.BatchSize.Should().Be(options.BatchSize); result.Collation.Should().BeSameAs(options.Collation); @@ -86,7 +86,7 @@ public void CreateChangeStreamOperation_for_database_returns_expected_result() var messageEncoderSettings = new MessageEncoderSettings(); var renderedPipeline = RenderPipeline(pipeline); - var result = ChangeStreamHelper.CreateChangeStreamOperation(mockDatabase.Object, pipeline, options, readConcern, messageEncoderSettings, retryRequested: true, translationOptions: null); + var result = ChangeStreamHelper.CreateChangeStreamOperation(mockDatabase.Object, pipeline, options, readConcern, messageEncoderSettings, retryRequested: true, translationOptions: null, BsonSerializer.DefaultSerializationDomain); result.BatchSize.Should().Be(options.BatchSize); result.Collation.Should().BeSameAs(options.Collation); @@ -129,7 +129,7 @@ public void CreateChangeStreamOperation_for_collection_returns_expected_result() var messageEncoderSettings = new MessageEncoderSettings(); var renderedPipeline = RenderPipeline(pipeline); - var result = ChangeStreamHelper.CreateChangeStreamOperation(mockCollection.Object, pipeline, documentSerializer, options, readConcern, messageEncoderSettings, retryRequested: true, translationOptions: null); + var result = ChangeStreamHelper.CreateChangeStreamOperation(mockCollection.Object, pipeline, documentSerializer, options, readConcern, messageEncoderSettings, retryRequested: true, translationOptions: null, BsonSerializer.DefaultSerializationDomain); result.BatchSize.Should().Be(options.BatchSize); result.Collation.Should().BeSameAs(options.Collation); diff --git a/tests/MongoDB.Driver.Tests/Core/IAsyncCursorExtensionsTests.cs b/tests/MongoDB.Driver.Tests/Core/IAsyncCursorExtensionsTests.cs index aff630021b5..ff7f8bc0ed9 100644 --- a/tests/MongoDB.Driver.Tests/Core/IAsyncCursorExtensionsTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/IAsyncCursorExtensionsTests.cs @@ -18,6 +18,7 @@ using System.Linq; using FluentAssertions; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; @@ -291,6 +292,7 @@ private IAsyncCursor CreateCursor(int count) limit: null, serializer: BsonDocumentSerializer.Instance, messageEncoderSettings: new MessageEncoderSettings(), + serializationDomain: BsonSerializer.DefaultSerializationDomain, maxTime: null); } } diff --git a/tests/MongoDB.Driver.Tests/Core/IAsyncCursorSourceExtensionsTests.cs b/tests/MongoDB.Driver.Tests/Core/IAsyncCursorSourceExtensionsTests.cs index e2b8188ce43..2d7ff839a60 100644 --- a/tests/MongoDB.Driver.Tests/Core/IAsyncCursorSourceExtensionsTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/IAsyncCursorSourceExtensionsTests.cs @@ -20,6 +20,7 @@ using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.TestHelpers.XunitExtensions; using MongoDB.Driver.Core.Bindings; @@ -292,6 +293,7 @@ private IAsyncCursor CreateCursor(int count) limit: null, serializer: BsonDocumentSerializer.Instance, messageEncoderSettings: new MessageEncoderSettings(), + serializationDomain: BsonSerializer.DefaultSerializationDomain, maxTime: null); } diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/AggregateToCollectionOperationTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/AggregateToCollectionOperationTests.cs index b45edc47149..8e48d8341bf 100644 --- a/tests/MongoDB.Driver.Tests/Core/Operations/AggregateToCollectionOperationTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Operations/AggregateToCollectionOperationTests.cs @@ -20,6 +20,7 @@ using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Misc; using MongoDB.Driver.Core.Servers; @@ -120,7 +121,7 @@ public void Constructor_should_throw_when_pipeline_is_null() [Fact] public void Constructor_should_throw_when_pipeline_does_not_end_with_out() { - var exception = Record.Exception(() => new AggregateToCollectionOperation(_collectionNamespace, Enumerable.Empty(), _messageEncoderSettings)); + var exception = Record.Exception(() => new AggregateToCollectionOperation(_collectionNamespace, Enumerable.Empty(), _messageEncoderSettings, BsonSerializer.DefaultSerializationDomain)); var argumentException = exception.Should().BeOfType().Subject; argumentException.ParamName.Should().Be("pipeline"); @@ -129,7 +130,7 @@ public void Constructor_should_throw_when_pipeline_does_not_end_with_out() [Fact] public void Constructor_should_throw_when_messageEncoderSettings_is_null() { - var exception = Record.Exception(() => new AggregateToCollectionOperation(_collectionNamespace, __pipeline, null)); + var exception = Record.Exception(() => new AggregateToCollectionOperation(_collectionNamespace, __pipeline, null, BsonSerializer.DefaultSerializationDomain)); var argumentNullException = exception.Should().BeOfType().Subject; argumentNullException.ParamName.Should().Be("messageEncoderSettings"); diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/AsyncCursorTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/AsyncCursorTests.cs index 3728762b355..e525c904990 100644 --- a/tests/MongoDB.Driver.Tests/Core/Operations/AsyncCursorTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Operations/AsyncCursorTests.cs @@ -443,7 +443,8 @@ public void GetMore_should_use_same_session( null, CommandResponseHandling.Return, It.IsAny>(), - It.IsAny())) + It.IsAny(), + It.IsAny())) .Callback(() => sameSessionWasUsed = true) .Returns(Task.FromResult(secondBatch)); @@ -465,8 +466,9 @@ public void GetMore_should_use_same_session( null, CommandResponseHandling.Return, It.IsAny>(), - It.IsAny())) - .Callback(() => sameSessionWasUsed = true) + It.IsAny(), + It.IsAny())) + .Callback(() => sameSessionWasUsed = true) .Returns(secondBatch); subject.MoveNext(CancellationToken.None); @@ -607,7 +609,8 @@ private void VerifyHowManyTimesKillCursorsCommandWasCalled(Mock It.IsAny>(), It.IsAny(), It.IsAny>(), - It.IsAny()), + It.IsAny(), + It.IsAny()), times); } else @@ -625,7 +628,8 @@ private void VerifyHowManyTimesKillCursorsCommandWasCalled(Mock It.IsAny>(), It.IsAny(), It.IsAny>(), - It.IsAny()), + It.IsAny(), + It.IsAny()), times); } } diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/ChangeStreamOperationTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/ChangeStreamOperationTests.cs index 394685a8807..d6bdd0363c4 100644 --- a/tests/MongoDB.Driver.Tests/Core/Operations/ChangeStreamOperationTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Operations/ChangeStreamOperationTests.cs @@ -86,7 +86,7 @@ public void constructor_with_database_should_throw_when_pipeline_is_null() var messageEncoderSettings = new MessageEncoderSettings(); - var exception = Record.Exception(() => new ChangeStreamOperation(databaseNamespace, pipeline, resultSerializer, messageEncoderSettings)); + var exception = Record.Exception(() => new ChangeStreamOperation(databaseNamespace, pipeline, resultSerializer, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain)); var argumentNullException = exception.Should().BeOfType().Subject; argumentNullException.ParamName.Should().Be("resultSerializer"); @@ -101,7 +101,7 @@ public void constructor_with_database_should_throw_when_messageEncoderSettings_i MessageEncoderSettings messageEncoderSettings = null; - var exception = Record.Exception(() => new ChangeStreamOperation(databaseNamespace, pipeline, resultSerializer, messageEncoderSettings)); + var exception = Record.Exception(() => new ChangeStreamOperation(databaseNamespace, pipeline, resultSerializer, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain)); var argumentNullException = exception.Should().BeOfType().Subject; argumentNullException.ParamName.Should().Be("messageEncoderSettings"); @@ -116,7 +116,7 @@ public void constructor_with_database_should_throw_when_resultSerializer_is_null var messageEncoderSettings = new MessageEncoderSettings(); - var exception = Record.Exception(() => new ChangeStreamOperation(databaseNamespace, pipeline, resultSerializer, messageEncoderSettings)); + var exception = Record.Exception(() => new ChangeStreamOperation(databaseNamespace, pipeline, resultSerializer, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain)); var argumentNullException = exception.Should().BeOfType().Subject; argumentNullException.ParamName.Should().Be("pipeline"); @@ -130,7 +130,7 @@ public void constructor_with_collection_should_initialize_instance() var resultSerializer = BsonDocumentSerializer.Instance; var messageEncoderSettings = new MessageEncoderSettings(); - var subject = new ChangeStreamOperation(collectionNamespace, pipeline, resultSerializer, messageEncoderSettings); + var subject = new ChangeStreamOperation(collectionNamespace, pipeline, resultSerializer, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain); subject.BatchSize.Should().NotHaveValue(); subject.Collation.Should().BeNull(); @@ -173,7 +173,7 @@ public void constructor_with_collection_should_throw_when_pipeline_is_null() var messageEncoderSettings = new MessageEncoderSettings(); - var exception = Record.Exception(() => new ChangeStreamOperation(collectionNamespace, pipeline, resultSerializer, messageEncoderSettings)); + var exception = Record.Exception(() => new ChangeStreamOperation(collectionNamespace, pipeline, resultSerializer, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain)); var argumentNullException = exception.Should().BeOfType().Subject; argumentNullException.ParamName.Should().Be("resultSerializer"); @@ -203,7 +203,7 @@ public void constructor_with_collection_should_throw_when_resultSerializer_is_nu var messageEncoderSettings = new MessageEncoderSettings(); - var exception = Record.Exception(() => new ChangeStreamOperation(collectionNamespace, pipeline, resultSerializer, messageEncoderSettings)); + var exception = Record.Exception(() => new ChangeStreamOperation(collectionNamespace, pipeline, resultSerializer, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain)); var argumentNullException = exception.Should().BeOfType().Subject; argumentNullException.ParamName.Should().Be("pipeline"); @@ -475,7 +475,7 @@ public void Execute_should_return_expected_results_for_inserts( var pipeline = new[] { BsonDocument.Parse("{ $match : { operationType : \"insert\" } }") }; var resultSerializer = new ChangeStreamDocumentSerializer(BsonDocumentSerializer.Instance); var messageEncoderSettings = new MessageEncoderSettings(); - var subject = new ChangeStreamOperation>(_collectionNamespace, pipeline, resultSerializer, messageEncoderSettings); + var subject = new ChangeStreamOperation>(_collectionNamespace, pipeline, resultSerializer, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain); EnsureDatabaseExists(); DropCollection(); Insert("{ _id : 1, x : 1 }"); diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/CreateCollectionOperationTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/CreateCollectionOperationTests.cs index a89f27c1a0a..f451aeb4c4a 100644 --- a/tests/MongoDB.Driver.Tests/Core/Operations/CreateCollectionOperationTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Operations/CreateCollectionOperationTests.cs @@ -19,6 +19,7 @@ using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Driver.Core.Bindings; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.TestHelpers.XunitExtensions; @@ -380,7 +381,7 @@ public void CreateCommand_should_return_expected_result_when_WriteConcern_is_set [Fact] public void CreateEncryptedCreateCollectionOperationIfConfigured_should_return_expected_result_when_EncryptedFields_is_null() { - var subject = CreateCollectionOperation.CreateEncryptedCreateCollectionOperationIfConfigured(_collectionNamespace, encryptedFields: null, _messageEncoderSettings, null); + var subject = CreateCollectionOperation.CreateEncryptedCreateCollectionOperationIfConfigured(_collectionNamespace, encryptedFields: null, _messageEncoderSettings, BsonSerializer.DefaultSerializationDomain, null); var session = OperationTestHelper.CreateSession(); var s = subject.Should().BeOfType().Subject; @@ -417,7 +418,7 @@ public void CreateEncryptedCreateCollectionOperationIfConfigured_should_return_e }}] }}"); - var subject = CreateCollectionOperation.CreateEncryptedCreateCollectionOperationIfConfigured(_collectionNamespace, encryptedFields, _messageEncoderSettings, null); + var subject = CreateCollectionOperation.CreateEncryptedCreateCollectionOperationIfConfigured(_collectionNamespace, encryptedFields, _messageEncoderSettings, BsonSerializer.DefaultSerializationDomain, null); var session = OperationTestHelper.CreateSession(); var operations = ((CompositeWriteOperation)subject)._operations(); diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/CursorBatchDeserializationHelperTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/CursorBatchDeserializationHelperTests.cs index c35abc52e0a..8fcced5cd2d 100644 --- a/tests/MongoDB.Driver.Tests/Core/Operations/CursorBatchDeserializationHelperTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Operations/CursorBatchDeserializationHelperTests.cs @@ -16,6 +16,7 @@ using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.IO; +using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; using Xunit; @@ -34,7 +35,7 @@ public void DeserializeBatch_should_return_expected_result_when_batch_has_one_do var documentSerializer = BsonDocumentSerializer.Instance; var messageEncoderSettings = new MessageEncoderSettings(); - var result = CursorBatchDeserializationHelper.DeserializeBatch(batch, documentSerializer, messageEncoderSettings); + var result = CursorBatchDeserializationHelper.DeserializeBatch(batch, documentSerializer, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain); result.Count.Should().Be(1); result[0].Should().BeOfType(); @@ -51,7 +52,7 @@ public void DeserializeBatch_should_return_expected_result_when_batch_has_two_do var documentSerializer = BsonDocumentSerializer.Instance; var messageEncoderSettings = new MessageEncoderSettings(); - var result = CursorBatchDeserializationHelper.DeserializeBatch(batch, documentSerializer, messageEncoderSettings); + var result = CursorBatchDeserializationHelper.DeserializeBatch(batch, documentSerializer, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain); result.Count.Should().Be(2); result[0].Should().BeOfType(); @@ -70,7 +71,7 @@ public void DeserializeBatch_should_return_expected_result_when_batch_is_empty() var documentSerializer = BsonDocumentSerializer.Instance; var messageEncoderSettings = new MessageEncoderSettings(); - var result = CursorBatchDeserializationHelper.DeserializeBatch(batch, documentSerializer, messageEncoderSettings); + var result = CursorBatchDeserializationHelper.DeserializeBatch(batch, documentSerializer, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain); result.Count.Should().Be(0); } @@ -86,7 +87,7 @@ public void DeserializeBatch_should_return_expected_result_when_GuidRepresentati var documentSerializer = BsonDocumentSerializer.Instance; var messageEncoderSettings = new MessageEncoderSettings { { "GuidRepresentation", GuidRepresentation.Standard } }; - var result = CursorBatchDeserializationHelper.DeserializeBatch(batch, documentSerializer, messageEncoderSettings); + var result = CursorBatchDeserializationHelper.DeserializeBatch(batch, documentSerializer, messageEncoderSettings, BsonSerializer.DefaultSerializationDomain); result.Count.Should().Be(1); result[0].Should().BeOfType(); diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/ReadCommandOperationTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/ReadCommandOperationTests.cs index 48b4bed12bf..5b3e9fa7e60 100644 --- a/tests/MongoDB.Driver.Tests/Core/Operations/ReadCommandOperationTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Operations/ReadCommandOperationTests.cs @@ -107,7 +107,8 @@ public void Execute_should_call_channel_Command_with_unwrapped_command_when_wrap null, // postWriteAction CommandResponseHandling.Return, subject.ResultSerializer, - subject.MessageEncoderSettings), + subject.MessageEncoderSettings, + It.IsAny()), Times.Once); } else @@ -125,7 +126,8 @@ public void Execute_should_call_channel_Command_with_unwrapped_command_when_wrap null, // postWriteAction CommandResponseHandling.Return, subject.ResultSerializer, - subject.MessageEncoderSettings), + subject.MessageEncoderSettings, + It.IsAny()), Times.Once); } } @@ -161,7 +163,8 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_additi null, // postWriteAction CommandResponseHandling.Return, subject.ResultSerializer, - subject.MessageEncoderSettings), + subject.MessageEncoderSettings, + It.IsAny()), Times.Once); } else @@ -179,7 +182,8 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_additi null, // postWriteAction CommandResponseHandling.Return, subject.ResultSerializer, - subject.MessageEncoderSettings), + subject.MessageEncoderSettings, + It.IsAny()), Times.Once); } } @@ -214,7 +218,8 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_commen null, // postWriteAction CommandResponseHandling.Return, subject.ResultSerializer, - subject.MessageEncoderSettings), + subject.MessageEncoderSettings, + It.IsAny()), Times.Once); } else @@ -232,7 +237,8 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_commen null, // postWriteAction CommandResponseHandling.Return, subject.ResultSerializer, - subject.MessageEncoderSettings), + subject.MessageEncoderSettings, + It.IsAny()), Times.Once); } } @@ -268,7 +274,8 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_readPr null, // postWriteAction CommandResponseHandling.Return, subject.ResultSerializer, - subject.MessageEncoderSettings), + subject.MessageEncoderSettings, + It.IsAny()), Times.Once); } else @@ -286,7 +293,8 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_readPr null, // postWriteAction CommandResponseHandling.Return, subject.ResultSerializer, - subject.MessageEncoderSettings), + subject.MessageEncoderSettings, + It.IsAny()), Times.Once); } } diff --git a/tests/MongoDB.Driver.Tests/Core/Operations/WriteCommandOperationTests.cs b/tests/MongoDB.Driver.Tests/Core/Operations/WriteCommandOperationTests.cs index 71c26aebdba..4d9b44e1815 100644 --- a/tests/MongoDB.Driver.Tests/Core/Operations/WriteCommandOperationTests.cs +++ b/tests/MongoDB.Driver.Tests/Core/Operations/WriteCommandOperationTests.cs @@ -81,7 +81,8 @@ public void Execute_should_call_channel_Command_with_unwrapped_command_when_wrap null, // postWriteAction CommandResponseHandling.Return, subject.ResultSerializer, - subject.MessageEncoderSettings), + subject.MessageEncoderSettings, + It.IsAny()), Times.Once); } else @@ -99,7 +100,8 @@ public void Execute_should_call_channel_Command_with_unwrapped_command_when_wrap null, // postWriteAction CommandResponseHandling.Return, subject.ResultSerializer, - subject.MessageEncoderSettings), + subject.MessageEncoderSettings, + It.IsAny()), Times.Once); } } @@ -133,7 +135,8 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_additi null, // postWriteAction CommandResponseHandling.Return, subject.ResultSerializer, - subject.MessageEncoderSettings), + subject.MessageEncoderSettings, + It.IsAny()), Times.Once); } else @@ -151,7 +154,8 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_additi null, // postWriteAction CommandResponseHandling.Return, subject.ResultSerializer, - subject.MessageEncoderSettings), + subject.MessageEncoderSettings, + It.IsAny()), Times.Once); } } @@ -185,7 +189,8 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_commen null, // postWriteAction CommandResponseHandling.Return, subject.ResultSerializer, - subject.MessageEncoderSettings), + subject.MessageEncoderSettings, + It.IsAny()), Times.Once); } else @@ -203,7 +208,8 @@ public void Execute_should_call_channel_Command_with_wrapped_command_when_commen null, // postWriteAction CommandResponseHandling.Return, subject.ResultSerializer, - subject.MessageEncoderSettings), + subject.MessageEncoderSettings, + It.IsAny()), Times.Once); } } diff --git a/tests/MongoDB.Driver.Tests/IMongoCollectionExtensionsTests.cs b/tests/MongoDB.Driver.Tests/IMongoCollectionExtensionsTests.cs index a65cf915b5c..f7ba678557b 100644 --- a/tests/MongoDB.Driver.Tests/IMongoCollectionExtensionsTests.cs +++ b/tests/MongoDB.Driver.Tests/IMongoCollectionExtensionsTests.cs @@ -20,6 +20,7 @@ using System.Threading; using FluentAssertions; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Driver.Tests.Linq.Linq3Implementation; using MongoDB.TestHelpers.XunitExtensions; using Moq; diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Linq3IntegrationTest.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Linq3IntegrationTest.cs index b198cf4e9a1..e61bffdae66 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Linq3IntegrationTest.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Linq3IntegrationTest.cs @@ -55,7 +55,7 @@ protected void CreateCollection(IMongoCollection collectio protected void CreateCollection(IMongoCollection collection, params TDocument[] documents) { - CreateCollection(collection, (IEnumerable)documents); ; + CreateCollection(collection, (IEnumerable)documents); } protected IMongoCollection GetCollection(string collectionName = null) diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionTranslators/ModuloComparisonExpressionToFilterTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionTranslators/ModuloComparisonExpressionToFilterTranslatorTests.cs index 10f3f2a5d14..c7c7cb9b4a3 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionTranslators/ModuloComparisonExpressionToFilterTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionTranslators/ModuloComparisonExpressionToFilterTranslatorTests.cs @@ -182,8 +182,9 @@ private void Assert(AstFilter result, string path, BsonValue divisor, BsonValue private TranslationContext CreateContext(ParameterExpression parameter) { - var serializer = BsonSerializer.LookupSerializer(parameter.Type); - var context = TranslationContext.Create(translationOptions: null); + var domain = BsonSerializer.DefaultSerializationDomain; + var serializer = domain.LookupSerializer(parameter.Type); + var context = TranslationContext.Create(translationOptions: null, domain); var symbol = context.CreateSymbol(parameter, serializer, isCurrent: true); return context.WithSymbol(symbol); } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/Translators/AggregateGroupTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/Translators/AggregateGroupTranslatorTests.cs index 45bbf7067af..3026ca52c67 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/Translators/AggregateGroupTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/Translators/AggregateGroupTranslatorTests.cs @@ -546,7 +546,8 @@ private ProjectedResult Group(Expression(Expression> expression, int { expression = (Expression>)PartialEvaluator.EvaluatePartially(expression); + var domain = BsonSerializer.DefaultSerializationDomain; var parameter = expression.Parameters.Single(); var serializer = BsonSerializer.LookupSerializer(); - var context = TranslationContext.Create(translationOptions: null); + var context = TranslationContext.Create(translationOptions: null, serializationDomain: domain); var symbol = context.CreateSymbol(parameter, serializer, isCurrent: true); context = context.WithSymbol(symbol); var filterAst = ExpressionToFilterTranslator.Translate(context, expression.Body); diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/Translators/PredicateTranslatorTests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/Translators/PredicateTranslatorTests.cs index 0869d70822e..1dbc964d7a8 100644 --- a/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/Translators/PredicateTranslatorTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3ImplementationWithLinq2Tests/Translators/PredicateTranslatorTests.cs @@ -1152,9 +1152,10 @@ public List Assert(IMongoCollection collection, { filter = (Expression>)PartialEvaluator.EvaluatePartially(filter); - var serializer = BsonSerializer.SerializerRegistry.GetSerializer(); + var domain = BsonSerializer.DefaultSerializationDomain; + var serializer = domain.SerializerRegistry.GetSerializer(); var parameter = filter.Parameters.Single(); - var context = TranslationContext.Create(translationOptions: null); + var context = TranslationContext.Create(translationOptions: null, serializationDomain: domain); var symbol = context.CreateSymbol(parameter, serializer, isCurrent: true); context = context.WithSymbol(symbol); var filterAst = ExpressionToFilterTranslator.Translate(context, filter.Body); diff --git a/tests/MongoDB.Driver.Tests/Linq/LinqProviderAdapterTests.cs b/tests/MongoDB.Driver.Tests/Linq/LinqProviderAdapterTests.cs index f6f0d15f647..789caacae8a 100644 --- a/tests/MongoDB.Driver.Tests/Linq/LinqProviderAdapterTests.cs +++ b/tests/MongoDB.Driver.Tests/Linq/LinqProviderAdapterTests.cs @@ -20,7 +20,6 @@ using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver.Linq; using MongoDB.Driver.Linq.Linq3Implementation; -using MongoDB.Driver.Tests; using MongoDB.Driver.Tests.Linq.Linq3Implementation; using Moq; using Xunit; @@ -48,10 +47,10 @@ public void AsQueryable_should_return_expected_result() public void TranslateExpressionToAggregateExpression_should_return_expected_result() { Expression> expression = c => c.X; - var serializerRegistry = BsonSerializer.SerializerRegistry; - var sourceSerializer = serializerRegistry.GetSerializer(); + var serializationDomain = BsonSerializer.DefaultSerializationDomain; + var sourceSerializer = serializationDomain.SerializerRegistry.GetSerializer(); - var result = LinqProviderAdapter.TranslateExpressionToAggregateExpression(expression, sourceSerializer, serializerRegistry, translationOptions: null); + var result = LinqProviderAdapter.TranslateExpressionToAggregateExpression(expression, sourceSerializer, serializationDomain, translationOptions: null); result.Should().Be("'$X'"); } @@ -60,10 +59,10 @@ public void TranslateExpressionToAggregateExpression_should_return_expected_resu public void TranslateExpressionToField_with_untyped_lambda_should_return_expected_result() { LambdaExpression expression = (Expression>)(c => c.X); - var serializerRegistry = BsonSerializer.SerializerRegistry; - var documentSerializer = serializerRegistry.GetSerializer(); + var serializationDomain = BsonSerializer.DefaultSerializationDomain; + var documentSerializer = serializationDomain.SerializerRegistry.GetSerializer(); - var result = LinqProviderAdapter.TranslateExpressionToField(expression, documentSerializer, serializerRegistry, translationOptions: null); + var result = LinqProviderAdapter.TranslateExpressionToField(expression, documentSerializer, serializationDomain, translationOptions: null); result.FieldName.Should().Be("X"); result.FieldSerializer.Should().BeOfType(typeof(Int32Serializer)); @@ -73,10 +72,10 @@ public void TranslateExpressionToField_with_untyped_lambda_should_return_expecte public void TranslateExpressionToField_with_typed_lambda_should_return_expected_result() { Expression> expression = c => c.X; - var serializerRegistry = BsonSerializer.SerializerRegistry; - var documentSerializer = serializerRegistry.GetSerializer(); + var serializationDomain = BsonSerializer.DefaultSerializationDomain; + var documentSerializer = serializationDomain.SerializerRegistry.GetSerializer(); - var result = LinqProviderAdapter.TranslateExpressionToField(expression, documentSerializer, serializerRegistry, translationOptions: null, allowScalarValueForArrayField: false); + var result = LinqProviderAdapter.TranslateExpressionToField(expression, documentSerializer, serializationDomain, translationOptions: null, allowScalarValueForArrayField: false); result.FieldName.Should().Be("X"); result.FieldSerializer.Should().BeOfType(typeof(Int32Serializer)); @@ -86,10 +85,10 @@ public void TranslateExpressionToField_with_typed_lambda_should_return_expected_ public void TranslateExpressionToFilter_should_return_expected_result() { Expression> expression = c => c.X == 0; - var serializerRegistry = BsonSerializer.SerializerRegistry; - var documentSerializer = serializerRegistry.GetSerializer(); + var serializationDomain = BsonSerializer.DefaultSerializationDomain; + var documentSerializer = serializationDomain.SerializerRegistry.GetSerializer(); - var result = LinqProviderAdapter.TranslateExpressionToFilter(expression, documentSerializer, serializerRegistry, translationOptions: null); + var result = LinqProviderAdapter.TranslateExpressionToFilter(expression, documentSerializer, serializationDomain, translationOptions: null); result.Should().Be("{ X : 0 }"); } @@ -98,10 +97,10 @@ public void TranslateExpressionToFilter_should_return_expected_result() public void TranslateExpressionToFindProjection_should_return_expected_result() { Expression> expression = c => c.X; - var serializerRegistry = BsonSerializer.SerializerRegistry; - var documentSerializer = serializerRegistry.GetSerializer(); + var serializationDomain = BsonSerializer.DefaultSerializationDomain; + var documentSerializer = serializationDomain.SerializerRegistry.GetSerializer(); - var result = LinqProviderAdapter.TranslateExpressionToFindProjection(expression, documentSerializer, serializerRegistry, translationOptions: null); + var result = LinqProviderAdapter.TranslateExpressionToFindProjection(expression, documentSerializer, serializationDomain, translationOptions: null); result.Document.Should().Be("{ X : 1, _id : 0 }"); result.ProjectionSerializer.ValueType.Should().Be(typeof(int)); @@ -114,10 +113,10 @@ public void TranslateExpressionToProjection_should_return_expected_result() void WithAnonymousOutputType(Expression> expression) { - var serializerRegistry = BsonSerializer.SerializerRegistry; - var inputSerializer = serializerRegistry.GetSerializer(); + var serializationDomain = BsonSerializer.DefaultSerializationDomain; + var inputSerializer = serializationDomain.SerializerRegistry.GetSerializer(); - var result = LinqProviderAdapter.TranslateExpressionToProjection(expression, inputSerializer, serializerRegistry, translationOptions: null); + var result = LinqProviderAdapter.TranslateExpressionToProjection(expression, inputSerializer, serializationDomain, translationOptions: null); result.Document.Should().Be("{ _id : '$_id', x : '$X' }"); result.ProjectionSerializer.ValueType.Should().Be(typeof(TOutput)); diff --git a/tests/MongoDB.Driver.Tests/MultipleRegistriesTests.cs b/tests/MongoDB.Driver.Tests/MultipleRegistriesTests.cs new file mode 100644 index 00000000000..5c7e655e134 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/MultipleRegistriesTests.cs @@ -0,0 +1,357 @@ +/* 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.Linq; +using MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Bson.Serialization.Conventions; +using MongoDB.Bson.Serialization.Serializers; +using MongoDB.Driver.Core.TestHelpers.XunitExtensions; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests +{ + [Trait("Category", "Integration")] + public class MultipleRegistriesTests + { + [Fact] + public void TestSerialization() + { + RequireServer.Check(); + + // { + // var client = CreateClient(); + // var collection = GetTypedCollection(client); + // var bsonCollection = GetUntypedCollection(client); + // + // var person = new Person { Id = ObjectId.Parse("6797b56bf5495bf53aa3078f"), Name = "Mario", Age = 24 }; + // collection.InsertOne(person); + // + // var retrieved = bsonCollection.FindSync(FilterDefinition.Empty).ToList().Single(); + // var toString = retrieved.ToString(); + // + // var expectedVal = + // """{ "_id" : { "$oid" : "6797b56bf5495bf53aa3078f" }, "Name" : "Mario", "Age" : 24 }"""; + // Assert.Equal(expectedVal, toString); + // } + + //The first section demonstrates that the class maps are also separated + { + var customDomain = BsonSerializer.CreateSerializationDomain(); + customDomain.RegisterSerializer(new CustomStringSerializer()); + + var client = CreateClientWithDomain(customDomain); + var collection = GetTypedCollection(client); + var bsonCollection = GetUntypedCollection(client); + + var person = new Person { Id = ObjectId.Parse("6797b56bf5495bf53aa3078f"), Name = "Mario", Age = 24 }; + collection.InsertOne(person); + + var retrievedAsBson = bsonCollection.FindSync(FilterDefinition.Empty).ToList().Single(); + var toString = retrievedAsBson.ToString(); + + var expectedVal = + """{ "_id" : { "$oid" : "6797b56bf5495bf53aa3078f" }, "Name" : "Mariotest", "Age" : 24 }"""; + Assert.Equal(expectedVal, toString); + + var retrievedTyped = collection.FindSync(FilterDefinition.Empty).ToList().Single(); + Assert.Equal("Mario", retrievedTyped.Name); + } + } + + [Fact] + public void TestDeserialization() + { + RequireServer.Check(); + + { + var client = CreateClient(); + var collection = GetTypedCollection(client); + + var person = new Person1 { Id = ObjectId.Parse("6797b56bf5495bf53aa3078f"), Name = "Mariotest", Age = 24 }; + collection.InsertOne(person); + } + + { + var customDomain = BsonSerializer.CreateSerializationDomain(); + customDomain.RegisterSerializer(new CustomStringSerializer()); + + var client = CreateClientWithDomain(customDomain, dropCollection: false); + var collection = GetTypedCollection(client); + + var retrievedTyped = collection.FindSync(FilterDefinition.Empty).ToList().Single(); + Assert.Equal("Mario", retrievedTyped.Name); + } + } + + [Fact] + public void TestLinq() + { + RequireServer.Check(); + + var customDomain = BsonSerializer.CreateSerializationDomain(); + customDomain.RegisterSerializer(new CustomStringSerializer()); + + var client = CreateClientWithDomain(customDomain); + var collection = GetTypedCollection(client); + var untypedCollection = GetUntypedCollection(client); + + var person = new Person { Id = ObjectId.Parse("6797b56bf5495bf53aa3078f"), Name = "Mario", Age = 24 }; + collection.InsertOne(person); + + var retrievedAsBson = untypedCollection.FindSync(FilterDefinition.Empty).ToList().Single(); + var toString = retrievedAsBson.ToString(); + + var expectedVal = + """{ "_id" : { "$oid" : "6797b56bf5495bf53aa3078f" }, "Name" : "Mariotest", "Age" : 24 }"""; + Assert.Equal(expectedVal, toString); + + var retrievedTyped = collection.AsQueryable().Where(x => x.Name == "Mario").ToList(); //The string serializer is correctly serializing "Mario" to "Mariotest" + Assert.NotEmpty(retrievedTyped); + } + + [Fact] + public void TestConventions() + { + RequireServer.Check(); + + var customDomain = BsonSerializer.CreateSerializationDomain(); + + // Register an id generator convention that uses a custom ObjectIdGenerator + customDomain.RegisterIdGenerator(typeof(ObjectId), new CustomObjectIdGenerator()); + + //Register a convention to use lowercase for all fields on the Person class + var pack = new ConventionPack(); + pack.AddMemberMapConvention( + "LowerCaseElementName", + m => m.SetElementName(m.MemberName.ToLower())); + customDomain.ConventionRegistry.Register("myPack", pack, t => t == typeof(Person)); + + var client = CreateClientWithDomain(customDomain); + var collection = GetTypedCollection(client); + var untypedCollection = GetUntypedCollection(client); + + var person = new Person { Name = "Mario", Age = 24 }; //Id is not set, so the custom ObjectIdGenerator should be used + collection.InsertOne(person); + + var retrievedAsBson = untypedCollection.FindSync(FilterDefinition.Empty).ToList().Single(); + var toString = retrievedAsBson.ToString(); + + var expectedVal = + """{ "_id" : { "$oid" : "6797b56bf5495bf53aa3078f" }, "name" : "Mario", "age" : 24 }"""; + Assert.Equal(expectedVal, toString); + } + + [Fact] + public void TestDiscriminators() + { + RequireServer.Check(); + + var customDomain = BsonSerializer.CreateSerializationDomain(); + + customDomain.BsonClassMap.RegisterClassMap(cm => + { + cm.AutoMap(); + cm.SetIsRootClass(true); + }); + + customDomain.BsonClassMap.RegisterClassMap(cm => + { + cm.AutoMap(); + cm.SetDiscriminator("dp1"); + cm.MapMember( m => m.ExtraField1).SetSerializer(new CustomStringSerializer()); + }); + + customDomain.BsonClassMap.RegisterClassMap(cm => + { + cm.AutoMap(); + cm.SetDiscriminator("dp2"); + cm.MapMember( m => m.ExtraField2).SetSerializer(new CustomStringSerializer()); + }); + + var client = CreateClientWithDomain(customDomain); + var collection = GetTypedCollection(client); + + var bp1 = new DerivedPerson1 { Name = "Alice", Age = 30, ExtraField1 = "Field1" }; + var bp2 = new DerivedPerson2 { Name = "Bob", Age = 40, ExtraField2 = "Field2" }; + collection.InsertMany(new BasePerson[] { bp1, bp2 }); + + //Aggregate with OfType + var retrievedDerivedPerson1 = collection.Aggregate().OfType().Single(); + var retrievedDerivedPerson2 = collection.Aggregate().OfType().Single(); + + AssertDerivedPerson1(bp1, retrievedDerivedPerson1); + AssertDerivedPerson2(bp2, retrievedDerivedPerson2); + + //AppendStage with OfType + retrievedDerivedPerson1 = collection.AsQueryable().AppendStage(PipelineStageDefinitionBuilder.OfType()) + .OfType().Single(); + retrievedDerivedPerson2 = collection.AsQueryable().AppendStage(PipelineStageDefinitionBuilder.OfType()) + .OfType().Single(); + + AssertDerivedPerson1(bp1, retrievedDerivedPerson1); + AssertDerivedPerson2(bp2, retrievedDerivedPerson2); + + //LINQ with OfType + retrievedDerivedPerson1 = collection.AsQueryable().OfType().Single(); + retrievedDerivedPerson2 = collection.AsQueryable().OfType().Single(); + + AssertDerivedPerson1(bp1, retrievedDerivedPerson1); + AssertDerivedPerson2(bp2, retrievedDerivedPerson2); + + //Facet with OfType + + var pipeline1 = PipelineDefinition.Create( new [] { + PipelineStageDefinitionBuilder.OfType() }); + var facet1 = AggregateFacet.Create("facet1", pipeline1); + + var pipeline2 = PipelineDefinition.Create( new [] { + PipelineStageDefinitionBuilder.OfType() }); + var facet2 = AggregateFacet.Create("facet2", pipeline2); + + var result = collection.Aggregate().Facet(facet1, facet2).Single().Facets; + retrievedDerivedPerson1 = result[0].Output().Single(); + retrievedDerivedPerson2 = result[1].Output().Single(); + + AssertDerivedPerson1(bp1, retrievedDerivedPerson1); + AssertDerivedPerson2(bp2, retrievedDerivedPerson2); + + //Find with OfType + var retrievedBasePerson1 = collection.FindSync(Builders.Filter.OfType()).Single(); + var retrievedBasePerson2 = collection.FindSync(Builders.Filter.OfType()).Single(); + + AssertBasePerson(bp1, retrievedBasePerson1); + AssertBasePerson(bp2, retrievedBasePerson2); + + void AssertDerivedPerson1(DerivedPerson1 expected, DerivedPerson1 retrieved) + { + AssertBasePerson(expected, retrieved); + Assert.Equal(expected.ExtraField1, retrieved.ExtraField1); + } + + void AssertDerivedPerson2(DerivedPerson2 expected, DerivedPerson2 retrieved) + { + AssertBasePerson(expected, retrieved); + Assert.Equal(expected.ExtraField2, retrieved.ExtraField2); + } + + void AssertBasePerson(BasePerson expected, BasePerson retrieved) + { + Assert.Equal(expected.Id, retrieved.Id); + Assert.Equal(expected.Name, retrieved.Name); + Assert.Equal(expected.Age, retrieved.Age); + } + } + + private static IMongoCollection GetTypedCollection(IMongoClient client) => + client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName) + .GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName); + + private static IMongoCollection GetUntypedCollection(IMongoClient client) => + client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName) + .GetCollection(DriverTestConfiguration.CollectionNamespace.CollectionName); + + private static IMongoClient CreateClientWithDomain(IBsonSerializationDomain domain, bool dropCollection = true) + { + var client = DriverTestConfiguration.CreateMongoClient((MongoClientSettings c) => ((IInheritableMongoClientSettings)c).SerializationDomain = domain); + if (dropCollection) + { + var db = client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); + db.DropCollection(DriverTestConfiguration.CollectionNamespace.CollectionName); + } + return client; + } + + private static IMongoClient CreateClient() + { + var client = DriverTestConfiguration.CreateMongoClient(); + var db = client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); + db.DropCollection(DriverTestConfiguration.CollectionNamespace.CollectionName); + return client; + } + + public class Person + { + [BsonId] public ObjectId Id { get; set; } + public string Name { get; set; } + public int Age { get; set; } + } + + public class Person1 + { + [BsonId] public ObjectId Id { get; set; } + public string Name { get; set; } + public int Age { get; set; } + } + + public class BasePerson + { + [BsonId] public ObjectId Id { get; set; } = ObjectId.GenerateNewId(); + public string Name { get; set; } + public int Age { get; set; } + } + + public class DerivedPerson1 : BasePerson + { + public string ExtraField1 { get; set; } + } + + public class DerivedPerson2 : BasePerson + { + public string ExtraField2 { get; set; } + } + + + public class CustomStringSerializer : SealedClassSerializerBase //This serializer just adds "test" to any serialised string + { + /// + public override int GetHashCode() => 0; + + protected override string DeserializeValue(BsonDeserializationContext context, BsonDeserializationArgs args) + { + var bsonReader = context.Reader; + + var bsonType = bsonReader.GetCurrentBsonType(); + return bsonType switch + { + BsonType.String => bsonReader.ReadString().Replace("test", ""), + _ => throw CreateCannotDeserializeFromBsonTypeException(bsonType) + }; + } + + protected override void SerializeValue(BsonSerializationContext context, BsonSerializationArgs args, + string value) + { + var bsonWriter = context.Writer; + bsonWriter.WriteString(value + "test"); + } + } + + public class CustomObjectIdGenerator : IIdGenerator + { + public object GenerateId(object container, object document) + { + return ObjectId.Parse("6797b56bf5495bf53aa3078f"); + } + + public bool IsEmpty(object id) + { + return true; + } + } + } +} \ No newline at end of file