-
Type:
Bug
-
Resolution: Done
-
Priority:
Major - P3
-
None
-
Affects Version/s: 2.8.1
-
Component/s: Serialization
-
None
-
Environment:.NET Core 2.0
-
None
-
None
-
None
-
None
-
None
-
None
-
None
I have a base class with a property named Type meant to be used as a discriminator, but that can be used in the C# code to tell what we are dealing with. Say we are dealing with vehicles:
public class abstract Vehicle
}
public enum VehicleType
This class is inherited by other classes:
public class Car: Vehicle
public class Truck: Vehicle
{ }
I have a collection of Vehicle in the database that can contain documents of any subclass that inherits from it.
var collection = mongoDatabase.GetCollection<Vehicle>("myVehicles");
So I register a discriminator convention to be able to serialize or deserialize the documents properly, using the Type property of Vehicle*:*
BsonSerializer.RegisterDiscriminatorConvention(typeof(Vehicle), new ScalarDiscriminatorConvention(nameof(Vehicle.Type)));
// Note: serialization convention being used for enums is string.
BsonSerializer.RegisterDiscriminator(typeof(Car), VehicleType.Car.ToString());
BsonSerializer.RegisterDiscriminator(typeof(Truck), VehicleType.Truck.ToString());
Now if later on I want to deserialize documents from the database into C#, it works great. I get documents instantiated to C# objects in the correct type and everything is populated correctly inside of them.
But if I insert something into the collection:
var myTruck = new Truck { Type = VehicleType.BigTruck };
mongoDatabase.GetCollection<Vehicle>.InsertOneAsync(myTruck);
What gets inserted the database is the following:
The Type field is put twice. I have been integrating the source code of MongoDB C# driver v2.8.1 to investigate why.
What I found is in the class BsonClassMapSerializer<TClass>, no check is made if the class already contains a property with the same name as the discriminator:
if (ShouldSerializeDiscriminator(args.NominalType))
[...]
private bool ShouldSerializeDiscriminator(Type nominalType)
So it seems to be expected by the serializer that if a discriminator is used, that this discriminator is not to be explicitly declared in the nominal type.
Because of that expectation, it gets serialized a first time because the property is there, just like any other property. But then it gets added again by the serializer because it determines the object requires discrimination.
I have figured out a few workarounds in order to be able to explicitly declare the discriminator without having it being serialized twice.
For example, from what I see here:
private void SerializeDiscriminator(BsonSerializationContext context, Type nominalType, object obj)
{
var discriminatorConvention = _classMap.GetDiscriminatorConvention();
if (discriminatorConvention != null)
{
var actualType = obj.GetType();
var discriminator = discriminatorConvention.GetDiscriminator(nominalType, actualType);
if (discriminator != null)
}
}
I see that if I implement my own custom discriminator convention and make its GetDiscriminator() call to return null when the nominal type is Vehicle*, it will skip serializing the discriminator.*
But it seems to me like the serialization code of the driver should check for this to avoid serializing the discriminator twice in the same document.