Search code examples
c#mongodbmongodb-.net-drivermongodb-csharp-2.0

How do I register a ScalarDiscriminatorConvention for a different field name in MongoDB?


I'm writing C# code to pull data out of an existing MongoDB database and put it into various C# classes. One part of the database is polymorphic: if the field type is A, then a certain set of fields will exist in that document. If type is B, a different set of fields will exist. That's the perfect use case for ScalarDiscriminatorConvention, so I'm using ScalarDiscriminatorConvention("type"). But I don't know how to make the MongoDB driver use that convention!

The MongoDB manual page on conventions made me think that this would work:

var pack = new ConventionPack();
pack.Add(new CamelCaseElementNameConvention());
pack.Add(new ScalarDiscriminatorConvention("type"));

ConventionRegistry.Register(
    "My Custom Conventions",
    pack,
    t => t.FullName.StartsWith("MyNamespace."));

But this fails because ScalarDiscriminatorConvention doesn't derive from the IConvention interface like the other conventions do. It derives from IDiscriminatorConvention, which is its own interface that does not derive from IConvention. And ConventionPack.Add expects an IConvention parameter.

So how do I register a different discriminator field? I've hunted through the MongoDB manual for at least an hour now and I'm quite at a loss. They don't seem to have it documented anywhere that I could find.


Solution

  • Discriminator conventions are separate from class mapping conventions. I'm assuming that your classes A and B derive from a common base class:

    public class BaseClass
    {
        public int Id;
    }
    
    public class A : BaseClass
    {
        public int FA;
    }
    
    public class B : BaseClass
    {
        public int FB;
    }
    

    I'm also assuming that you want the discriminator field name to be "type" instead of the default of "_t".

    You would register a discriminator convention for the base class like this:

    BsonSerializer.RegisterDiscriminatorConvention(typeof(BaseClass), new ScalarDiscriminatorConvention("type"));
    

    You can now serialize and deserialize BaseClass values and the "type" field will contain either "A" or "B" depending on the actual type of the value.

    I used the following code to verify the serialized form and to verify that the serialized form could be deserialized:

    BaseClass a = new A { Id = 1, FA = 1 };
    var jsonA = a.ToJson();
    var deserializedA = BsonSerializer.Deserialize<BaseClass>(jsonA);
    
    BaseClass b = new B { Id = 2, FB = 2 };
    var jsonB = b.ToJson();
    var deserializedB = BsonSerializer.Deserialize<BaseClass>(jsonB);