Skip to content

Commit 3a33efa

Browse files
authored
CSHARP-5846: Global GuidSerializer settings ignored for object-typed properties (#1990)
1 parent 8dcbedf commit 3a33efa

2 files changed

Lines changed: 135 additions & 5 deletions

File tree

src/MongoDB.Bson/Serialization/Serializers/ObjectSerializer.cs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public sealed class ObjectSerializer : ClassSerializerBase<object>, IHasDiscrimi
6060
private readonly Func<Type, bool> _allowedSerializationTypes;
6161
private readonly IDiscriminatorConvention _discriminatorConvention;
6262
private readonly GuidRepresentation _guidRepresentation;
63-
private readonly GuidSerializer _guidSerializer;
63+
private readonly Lazy<IBsonSerializer<Guid>> _guidSerializer;
6464

6565
// constructors
6666
/// <summary>
@@ -135,10 +135,17 @@ public ObjectSerializer(
135135
Func<Type, bool> allowedSerializationTypes)
136136
{
137137
_discriminatorConvention = discriminatorConvention ?? throw new ArgumentNullException(nameof(discriminatorConvention));
138-
_guidRepresentation = guidRepresentation;
139-
_guidSerializer = new GuidSerializer(_guidRepresentation);
140138
_allowedDeserializationTypes = allowedDeserializationTypes ?? throw new ArgumentNullException(nameof(allowedDeserializationTypes));
141139
_allowedSerializationTypes = allowedSerializationTypes ?? throw new ArgumentNullException(nameof(allowedSerializationTypes));
140+
_guidRepresentation = guidRepresentation;
141+
if (guidRepresentation != GuidRepresentation.Unspecified)
142+
{
143+
_guidSerializer = new Lazy<IBsonSerializer<Guid>>(() => new GuidSerializer(guidRepresentation));
144+
}
145+
else
146+
{
147+
_guidSerializer = new Lazy<IBsonSerializer<Guid>>(() => BsonSerializer.LookupSerializer<Guid>());
148+
}
142149
}
143150

144151
// public properties
@@ -190,7 +197,7 @@ public override object Deserialize(BsonDeserializationContext context, BsonDeser
190197
if (subType == BsonBinarySubType.UuidStandard || subType == BsonBinarySubType.UuidLegacy)
191198
{
192199
bsonReader.ReturnToBookmark(binaryDataBookmark);
193-
return _guidSerializer.Deserialize(context);
200+
return _guidSerializer.Value.Deserialize(context);
194201
}
195202
goto default;
196203

@@ -317,7 +324,7 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati
317324
if (actualType == typeof(Guid))
318325
{
319326
var guid = (Guid)value;
320-
_guidSerializer.Serialize(context, args, guid);
327+
_guidSerializer.Value.Serialize(context, args, guid);
321328
return;
322329
}
323330
if (actualType == typeof(ObjectId))

tests/MongoDB.Bson.Tests/Serialization/Serializers/ObjectSerializerTests.cs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414
*/
1515

1616
using System;
17+
using System.Collections.Concurrent;
1718
using System.Dynamic;
1819
using System.IO;
1920
using System.Linq;
21+
using System.Reflection;
2022
using FluentAssertions;
2123
using MongoDB.Bson.IO;
2224
using MongoDB.Bson.Serialization;
@@ -725,6 +727,127 @@ public void Serialize_guid_should_throw_when_guid_representation_is_unspecified(
725727
exception.Should().BeOfType<BsonSerializationException>();
726728
}
727729
}
730+
731+
[Fact]
732+
public void Deserialize_binary_data_should_use_globally_registered_guid_serializer_when_guid_representation_is_unspecified()
733+
{
734+
var discriminatorConvention = BsonSerializer.LookupDiscriminatorConvention(typeof(object));
735+
var guid = Guid.NewGuid();
736+
var bson = new BsonDocument("x", new BsonBinaryData(guid, GuidRepresentation.CSharpLegacy)).ToBson();
737+
object result = null;
738+
739+
WithRegisteredGuidSerializer(GuidRepresentation.CSharpLegacy, () =>
740+
{
741+
var subject = new ObjectSerializer(discriminatorConvention, GuidRepresentation.Unspecified);
742+
743+
using var memoryStream = new MemoryStream(bson);
744+
using var reader = new BsonBinaryReader(memoryStream);
745+
var context = BsonDeserializationContext.CreateRoot(reader);
746+
reader.ReadStartDocument();
747+
reader.ReadName("x");
748+
result = subject.Deserialize<object>(context);
749+
});
750+
751+
result.Should().Be(guid);
752+
}
753+
754+
[Fact]
755+
public void Deserialize_binary_data_should_use_guid_serializer_registered_after_object_serializer_is_constructed()
756+
{
757+
var discriminatorConvention = BsonSerializer.LookupDiscriminatorConvention(typeof(object));
758+
var subject = new ObjectSerializer(discriminatorConvention, GuidRepresentation.Unspecified);
759+
var guid = Guid.NewGuid();
760+
var bson = new BsonDocument("x", new BsonBinaryData(guid, GuidRepresentation.CSharpLegacy)).ToBson();
761+
object result = null;
762+
763+
WithRegisteredGuidSerializer(GuidRepresentation.CSharpLegacy, () =>
764+
{
765+
using var memoryStream = new MemoryStream(bson);
766+
using var reader = new BsonBinaryReader(memoryStream);
767+
var context = BsonDeserializationContext.CreateRoot(reader);
768+
reader.ReadStartDocument();
769+
reader.ReadName("x");
770+
result = subject.Deserialize<object>(context);
771+
});
772+
773+
result.Should().Be(guid);
774+
}
775+
776+
[Fact]
777+
public void Serialize_guid_should_use_globally_registered_guid_serializer_when_guid_representation_is_unspecified()
778+
{
779+
var discriminatorConvention = BsonSerializer.LookupDiscriminatorConvention(typeof(object));
780+
var guid = Guid.NewGuid();
781+
BsonBinaryData binaryData = null;
782+
783+
WithRegisteredGuidSerializer(GuidRepresentation.CSharpLegacy, () =>
784+
{
785+
var subject = new ObjectSerializer(discriminatorConvention, GuidRepresentation.Unspecified);
786+
787+
using var memoryStream = new MemoryStream();
788+
using var writer = new BsonBinaryWriter(memoryStream);
789+
var context = BsonSerializationContext.CreateRoot(writer);
790+
writer.WriteStartDocument();
791+
writer.WriteName("x");
792+
subject.Serialize(context, guid);
793+
writer.WriteEndDocument();
794+
795+
binaryData = BsonSerializer.Deserialize<BsonDocument>(memoryStream.ToArray())["x"].AsBsonBinaryData;
796+
});
797+
798+
binaryData.SubType.Should().Be(BsonBinarySubType.UuidLegacy);
799+
binaryData.ToGuid(GuidRepresentation.CSharpLegacy).Should().Be(guid);
800+
}
801+
802+
[Fact]
803+
public void Serialize_guid_should_use_guid_serializer_registered_after_object_serializer_is_constructed()
804+
{
805+
var discriminatorConvention = BsonSerializer.LookupDiscriminatorConvention(typeof(object));
806+
var subject = new ObjectSerializer(discriminatorConvention, GuidRepresentation.Unspecified);
807+
var guid = Guid.NewGuid();
808+
BsonBinaryData binaryData = null;
809+
810+
WithRegisteredGuidSerializer(GuidRepresentation.CSharpLegacy, () =>
811+
{
812+
using var memoryStream = new MemoryStream();
813+
using var writer = new BsonBinaryWriter(memoryStream);
814+
var context = BsonSerializationContext.CreateRoot(writer);
815+
writer.WriteStartDocument();
816+
writer.WriteName("x");
817+
subject.Serialize(context, guid);
818+
writer.WriteEndDocument();
819+
820+
binaryData = BsonSerializer.Deserialize<BsonDocument>(memoryStream.ToArray())["x"].AsBsonBinaryData;
821+
});
822+
823+
binaryData.SubType.Should().Be(BsonBinarySubType.UuidLegacy);
824+
binaryData.ToGuid(GuidRepresentation.CSharpLegacy).Should().Be(guid);
825+
}
826+
827+
private static void WithRegisteredGuidSerializer(GuidRepresentation registeredGuidRepresentation, Action action)
828+
{
829+
var cacheField = typeof(BsonSerializerRegistry).GetField("_cache", BindingFlags.NonPublic | BindingFlags.Instance);
830+
var cache = (ConcurrentDictionary<Type, IBsonSerializer>)cacheField.GetValue(BsonSerializer.SerializerRegistry);
831+
832+
cache.TryGetValue(typeof(Guid), out var savedSerializer);
833+
var newSerializer = new GuidSerializer(registeredGuidRepresentation);
834+
cache.AddOrUpdate(typeof(Guid), newSerializer, (_, __) => newSerializer);
835+
try
836+
{
837+
action();
838+
}
839+
finally
840+
{
841+
if (savedSerializer != null)
842+
{
843+
cache.AddOrUpdate(typeof(Guid), savedSerializer, (_, __) => savedSerializer);
844+
}
845+
else
846+
{
847+
cache.TryRemove(typeof(Guid), out _);
848+
}
849+
}
850+
}
728851
}
729852

730853
public class DefaultAllowedTypesTests

0 commit comments

Comments
 (0)