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

Discriminator not included when saving IReadOnlyList<T> properties

    • Type: Icon: Question Question
    • Resolution: Gone away
    • Priority: Icon: Unknown Unknown
    • None
    • Affects Version/s: 2.14.0
    • Component/s: Serialization
    • None

      There seems to be a bug when trying to serialize properties of IReadOnlyList<T> when the property is set to an array of a derived type.

       

      For example, running the following code:

      using MongoDB.Bson;
      using MongoDB.Driver;
      
      var client = new MongoClient();
      
      var database = client.GetDatabase("test");
      
      var collection = database.GetCollection<Farm>("farms");
      
      var farm = new Farm(
          ObjectId.GenerateNewId(),
          "Farm 1",
          new[] { new Cow(1, "Bob"), new Cow(2, "Alice") }
      );
      
      await collection.InsertOneAsync(farm);
      
      // Exception thrown here
      var foundFarm = await collection.Find(x => x.Id == farm.Id).FirstAsync();
      
      record Farm(ObjectId Id, string Name, IReadOnlyList<Animal> Animals);
      
      abstract record Animal(int Id);
      
      record Cow(int Id, string Name) : Animal(Id);
      
      record Pig(int Id, string Name) : Animal(Id);
      

      Throws the following exception:

      Unhandled exception. System.FormatException: An error occurred while deserializing the Animals property of class Farm: Cannot create an abstract class.
       ---> System.MemberAccessException: Cannot create an abstract class.
         at System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject(Type type)
         at System.Runtime.Serialization.FormatterServices.GetUninitializedObject(Type type)
         at MongoDB.Bson.Serialization.BsonClassMap.<GetCreator>b__114_2()
         at MongoDB.Bson.Serialization.BsonClassMapSerializer`1.DeserializeClass(BsonDeserializationContext context)
         at MongoDB.Bson.Serialization.BsonClassMapSerializer`1.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
         at MongoDB.Bson.Serialization.IBsonSerializerExtensions.Deserialize[TValue](IBsonSerializer`1 serializer, BsonDeserializationContext context)
         at MongoDB.Bson.Serialization.Serializers.EnumerableSerializerBase`2.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
         at MongoDB.Bson.Serialization.IBsonSerializerExtensions.Deserialize[TValue](IBsonSerializer`1 serializer, BsonDeserializationContext context)
         at MongoDB.Bson.Serialization.Serializers.ImpliedImplementationInterfaceSerializer`2.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
         at MongoDB.Bson.Serialization.Serializers.SerializerBase`1.MongoDB.Bson.Serialization.IBsonSerializer.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
         at MongoDB.Bson.Serialization.IBsonSerializerExtensions.Deserialize(IBsonSerializer serializer, BsonDeserializationContext context)
         at MongoDB.Bson.Serialization.BsonClassMapSerializer`1.DeserializeMemberValue(BsonDeserializationContext context, BsonMemberMap memberMap)
         --- End of inner exception stack trace ---
         at MongoDB.Bson.Serialization.BsonClassMapSerializer`1.DeserializeMemberValue(BsonDeserializationContext context, BsonMemberMap memberMap)
         at MongoDB.Bson.Serialization.BsonClassMapSerializer`1.DeserializeClass(BsonDeserializationContext context)
         at MongoDB.Bson.Serialization.BsonClassMapSerializer`1.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
         at MongoDB.Bson.Serialization.IBsonSerializerExtensions.Deserialize[TValue](IBsonSerializer`1 serializer, BsonDeserializationContext context)
         at MongoDB.Driver.Core.Operations.CursorBatchDeserializationHelper.DeserializeBatch[TDocument](RawBsonArray batch, IBsonSerializer`1 documentSerializer, MessageEncoderSettings messageEncoderSettings)
         at MongoDB.Driver.Core.Operations.FindCommandOperation`1.CreateFirstCursorBatch(BsonDocument cursorDocument)
         at MongoDB.Driver.Core.Operations.FindCommandOperation`1.CreateCursor(IChannelSourceHandle channelSource, IChannelHandle channel, BsonDocument commandResult)
         at MongoDB.Driver.Core.Operations.FindCommandOperation`1.ExecuteAsync(RetryableReadContext context, CancellationToken cancellationToken)
         at MongoDB.Driver.Core.Operations.FindOperation`1.ExecuteAsync(RetryableReadContext context, CancellationToken cancellationToken)
         at MongoDB.Driver.Core.Operations.FindOperation`1.ExecuteAsync(IReadBinding binding, CancellationToken cancellationToken)
         at MongoDB.Driver.OperationExecutor.ExecuteReadOperationAsync[TResult](IReadBinding binding, IReadOperation`1 operation, CancellationToken cancellationToken)
         at MongoDB.Driver.MongoCollectionImpl`1.ExecuteReadOperationAsync[TResult](IClientSessionHandle session, IReadOperation`1 operation, ReadPreference readPreference, CancellationToken cancellationToken)
         at MongoDB.Driver.MongoCollectionImpl`1.UsingImplicitSessionAsync[TResult](Func`2 funcAsync, CancellationToken cancellationToken)
         at MongoDB.Driver.IAsyncCursorSourceExtensions.FirstAsync[TDocument](IAsyncCursorSource`1 source, CancellationToken cancellationToken)
         at Program.<Main>$(String[] args) in C:\dev\kevbite\MongoDbBug\MongoDbBug\MongoDbBug\Program.cs:line 20
         at Program.<Main>(String[] args)
      

      ... and generates the following document in the database

      {
              "_id" : ObjectId("61a9036e68774829f484e844"),
              "Name" : "Farm 1",
              "Animals" : [
                      {
                              "_id" : 1,
                              "Name" : "Bob"
                      },
                      {
                              "_id" : 2,
                              "Name" : "Alice"
                      }
              ]
      }
      

      As you can see it's missing the discriminator field (`_t`)

      I assume it's because the array is of type `Cow[]` instead of `Animal[]`. If we update the code with the following change:

      var farm = new Farm(
          ObjectId.GenerateNewId(),
          "Farm 1",
          new Animal[] { new Cow(1, "Bob"), new Cow(2, "Alice") }
      );
      

      The following document is generated

      {
              "_id" : ObjectId("61a905ea132842f1fcf18782"),
              "Name" : "Farm 1",
              "Animals" : [
                      {
                              "_t" : "Cow",
                              "_id" : 1,
                              "Name" : "Bob"
                      },
                      {
                              "_t" : "Cow",
                              "_id" : 2,
                              "Name" : "Alice"
                      }
              ]
      }
      

            Assignee:
            james.kovacs@mongodb.com James Kovacs
            Reporter:
            kev_bite@msn.com Kevin Smith
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: