Uploaded image for project: 'C# Driver'
  1. C# Driver
  2. CSHARP-5413

Custom DiscriminatorConventions not recognized for de/serialization after update to Driver 3.0

    • Type: Icon: Bug Bug
    • Resolution: Duplicate
    • Priority: Icon: Unknown Unknown
    • None
    • Affects Version/s: 3.0.0
    • Component/s: None
    • None
    • None
    • Dotnet Drivers
    • Hide

      1. What would you like to communicate to the user about this feature?
      2. Would you like the user to see examples of the syntax and/or executable code and its output?
      3. Which versions of the driver/connector does this apply to?

      Show
      1. What would you like to communicate to the user about this feature? 2. Would you like the user to see examples of the syntax and/or executable code and its output? 3. Which versions of the driver/connector does this apply to?
    • None
    • None
    • None
    • None
    • None
    • None

      Summary

      BsonClassMapSerializer uses the discriminator convention registered in the class map and falls back to standard implementations regardless of discriminator conventions registered globally in the serializer registry.

      Please provide the version of the driver. If applicable, please provide the MongoDB server version and topology (standalone, replica set, or sharded cluster).

      Version 3.0

      How to Reproduce

      This is a sample XUnit test class to reproduce the issue.

       

      
      

      _namespace MongoDbTests;

      public class BaseClass;

      public class SubClassThatNormallyLivesSomewhereElseButWeDontCare : BaseClass;

      public class DiscriminatorConventionTests
      {
      public DiscriminatorConventionTests()
      {
      // This was sufficient previously
      var convention = new ObjectDiscriminatorConvention("_type");
      BsonSerializer.RegisterDiscriminatorConvention(
      typeof(BaseClass),
      convention);

      // This is now needed as well for the BsonClassMapSerialzer to get the correct discriminator convention
      BsonClassMap.TryRegisterClassMap<BaseClass>(
      map =>

      { map.AutoMap(); map.SetDiscriminatorConvention(convention); }

      );

      // But its still not sufficient, to make it work.
      // We have to register the SAME convention for EVERY potential sub type, which makes no sense.
      BsonClassMap.TryRegisterClassMap<SubClassThatNormallyLivesSomewhereElseButWeDontCare>(
      map =>
      {
      map.AutoMap();
      map.SetDiscriminatorConvention(convention);
      });
      }

      [Fact]
      public void DeserializeSubClassWorks()
      {
      // Fails with "Element '_type' does not match any field or property of class MongoDbTests.SubClassThatNormallyLivesSomewhereElseButWeDontCare."
      // if the Convention is not registered for the sub type.
      var mustBeSubClassInstance = BsonSerializer.Deserialize<BaseClass>(
      BsonDocument.Parse($"{{ _type: '

      {TypeNameDiscriminator.GetDiscriminator(typeof(SubClassThatNormallyLivesSomewhereElseButWeDontCare))}

      ' }}"));

      mustBeSubClassInstance.Should()
      .BeOfType<SubClassThatNormallyLivesSomewhereElseButWeDontCare>();
      }
      }_

       

       

      Additional Background

      After upgrading to the C# Driver 3.0 a lot of things changed regarding discriminator conventions that are not documented.

      We also had problems using OfType which could be solved by implementing the IHierarchicalDiscriminatorConvention interface and using registering our convention in the class map.

      For this particular problem here, imo the culprit is the line
      __

      if (discriminatorConvention == null)
      {
          discriminatorConvention = _hasRootClass ? StandardDiscriminatorConvention.Hierarchical : StandardDiscriminatorConvention.Scalar;
          _discriminatorConvention = discriminatorConvention;
      } 

      in the BsonClassMap.GetDiscriminatorConvention method which was
      __

      if (discriminatorConvention == null)
      {
          discriminatorConvention = BsonSerializer.LookupDiscriminatorConvention(_classType);
          _discriminatorConvention = discriminatorConvention;
      } 

      before.

      Registering custom discriminator conventions for a base class is useless in this case because a class map is created for every type and sub type and having this we are forced to register the exact same discriminator convention in every sub type class map to make it work.

      Usually in a complex environment classes are distributed across multiple modules, so at the place of the base class mapping where we register the discriminator convention we have no knowledge of every possible sub type, so we cannot use AddKnownType or things like that. But in fact we also don't care, because our discriminator convention is supposed to handle this.

      If for a reason resolving the discriminator convention of a class map really should fall back to the standard implementations and not the registry, it maybe should only do so if there is no base class map, otherwise should call _baseClassMap.GetDiscriminatorConvention()?

       

       

            Assignee:
            ferdinando.papale@mongodb.com Ferdinando Papale
            Reporter:
            robin.kaulfuss@qualitybytes.de Robin Kaulfuß
            Votes:
            1 Vote for this issue
            Watchers:
            7 Start watching this issue

              Created:
              Updated:
              Resolved: