Regression in v2.10.2 .NET driver

I created this issue in Jira: https://jira.mongodb.org/projects/CSHARP/issues/CSHARP-3175?

But from past experience I know that it might take time before someone looks at that issue, so I’m posting in here as well:

This is my Warehouse class:

public class Warehouse
{
	public string Id { get; private set; }
	public string Name { get; private set; }
	public LocationAddress? Address { get; private set; }
	public WarehouseShelfSetting? ShelfSettings { get; private set; }
	public WarehouseTrolleySettings? TrolleySettings { get; private set; }
 
	public Warehouse(string name)
	{
		this.Name = name;
		this.Id = ObjectId.GenerateNewId().ToString();
	}
 
	public Warehouse(string name, LocationAddress? address)
	{
		this.Name = name;
		this.Address = address;
		this.Id = ObjectId.GenerateNewId().ToString();
	}
 
	public Warehouse(string id, string name, LocationAddress? address)
	{
		this.Name = name;
		this.Address = address;
		this.Id = id;
	}
 
	public class WarehouseTrolleySettings
	{
		public LabellingStrategy SlotLabelling { get; set; }
		public int NumberOfSlots { get; set; }
	}
 
	public class WarehouseShelfSetting
	{
		public LabellingStrategy ShelfLabelling { get; set; }
		public LabellingStrategy SlotLabelling { get; set; }
	}
 
	public enum LabellingStrategy
	{
		Alphabetic,
		Numeric
	}
}

This is the document in the database:

{
    "_id" : ObjectId("5c534452d3224cc69bdcb6ac"),
    "Name" : "Centrallagret",
    "Address" : {
        "StreetAddress" : "Storgatan 1",
        "StreetAddress2" : null,
        "PostalCode" : "123 45",
        "City" : "Stockholm",
        "CountryCode" : "se"
    }
}

This code has been the same for very long time, but today I upgraded the MongoDB.Driver package, and now I get this exception:

System.FormatException: An error occurred while deserializing the Address property of class Zwiftly.Items.Warehouses.Warehouse: No matching creator found.
 ---> MongoDB.Bson.BsonSerializationException: No matching creator found.
   at MongoDB.Bson.Serialization.BsonClassMapSerializer`1.ChooseBestCreator(Dictionary`2 values)
   at MongoDB.Bson.Serialization.BsonClassMapSerializer`1.CreateInstanceUsingCreator(Dictionary`2 values)
   at MongoDB.Bson.Serialization.BsonClassMapSerializer`1.DeserializeClass(BsonDeserializationContext context)
   at MongoDB.Bson.Serialization.BsonClassMapSerializer`1.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.CreateCursorBatch(BsonDocument commandResult)
   at MongoDB.Driver.Core.Operations.FindCommandOperation`1.CreateCursor(IChannelSourceHandle channelSource, 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.FirstOrDefaultAsync[TDocument](IAsyncCursorSource`1 source, CancellationToken cancellationToken)

I backed version by version and found that this regression was introduced in version 2.10.2.

Welcome to the community forums @John_Knoop!

MongoDB’s JIRA is the relevant channel to report a bug or regression (or search for relevant behaviour changes) in an officially supported driver, however it may take some time for the driver team to triage new issues depending on other development priorities. I do see that you have reported other issues that have been triaged into the backlog without a public comment yet – you can always ask for an update by commenting on the issue.

The issue you reported is an intentional change introduced in the 2.10.2 driver via CSHARP-2889: BsonClassMap.LookupClassMap supports private constructors inconsistently.

For a more detailed explanation, please see @Robert_Stam’s comment on CSHARP-3108.

Paraphrasing Robert’s comment:

The exception is being thrown because the document does not have a value for an expected property and therefore we don’t have all the needed values to pass to the constructor.

You can tell the driver to use a default value for missing fields by using the [BsonDefaultValue] annotation

Regards,
Stennie

@Stennie_X

Thanks for your answer! But are you sure this is the same problem?

The comment you quote says “The exception is being thrown because the document does not have a value for an expected property and therefore we don’t have all the needed values to pass to the constructor.”, but thats not really the case here. My persisted document contains all the values needed to call any of the constructors.

Or does it actually look at the properties of the class, and not on the signature of the constructors?

@Stennie_X my mistake, this actually turned out to be the same issue.

Do you by any change know if it’s possible to use BsonDefaultValue(null) via a convention? So that I don’t have to sprinkle that attribute all over my domain model?