Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define a default discriminator for a polymorphic class mapping in C# MongoDB

There is some legacy data that I have refactored to have a polymorphic structure:

class Shape {}
class Rectangle : Shape {}

class MyClass
{
    public Shape shape;
}

BsonClassMap.RegisterClassMap<Shape>(cm =>
{
    cm.AutoMap();
    cm.SetIsRootClass(true);
    cm.AddKnownType(typeof(Rectangle));
});

This works great for new data, the discriminator _t is created for shapes and rectangles accordingly. The problem I'm having is with legacy data which is all of type rectangle, but has no discriminator _t in the db since when it was created no polymorphism existed. When I try to read one of these objects, I get a deserialization error because it cannot map the legacy Rectangle to a Shape. I would prefer not to have to run a data migration to add the discriminator, is there a way for me to default the deserialization type to Rectangle if the discriminator is missing?

like image 464
zaq Avatar asked Jan 28 '26 07:01

zaq


1 Answers

I figured this out, the solution is to implement a custom serializer to handle the deserialization, like this:

public class ShapeNullDiscriminatorSerializer : SerializerBase<Shape>
{
    public override Shape Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
    {
        if (context.Reader.CurrentBsonType == BsonType.Null)
        {
            context.Reader.ReadNull();
            return default(Shape);
        }
        var bsonDoc = BsonSerializer.Deserialize<BsonDocument>(context.Reader);

        if (!bsonDoc.Contains("_t"))
            return BsonSerializer.Deserialize<Rectangle>(bsonDoc);

        return BsonSerializer.Deserialize<Shape>(bsonDoc);
    }

    public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, FeedbackConfig value)
    {
        BsonSerializer.Serialize<Shape>(context.Writer, value, null, args);
    }
}

And then map it like this on the parent class:

BsonClassMap.RegisterClassMap<MyClass>(cm =>
{
    cm.AutoMap();
    cm.MapMember(mc => mc.shape).SetSerializer(new ShapeNullDiscriminatorSerializer());
});
like image 124
zaq Avatar answered Jan 29 '26 19:01

zaq



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!