[CSHARP-1339] Adding array member to collection model causes error during subsequent deserialisation Created: 28/Jun/15  Updated: 02/Apr/20  Resolved: 02/Apr/20

Status: Closed
Project: C# Driver
Component/s: Serialization
Affects Version/s: 2.0.1
Fix Version/s: None

Type: Bug Priority: Minor - P4
Reporter: Kieren Johnstone Assignee: Wan Bachtiar
Resolution: Done Votes: 6
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Depends
depends on CSHARP-2643 Automapped serializer for anonymous c... Closed

 Description   

Types:

 
        class ContainerV1
        {
            public string Id { get; set; }
            public bool A { get; set; }
        }
 
        class ContainerV2
        {
            public string Id { get; set; }
            public bool A { get; set; }
            public SubItem[] Blah { get; set; }
        }

Code:

            var mongo = new MongoClient("mongodb://localhost");
 
            // start from scratch
            mongo.DropDatabaseAsync("foo").GetAwaiter().GetResult();
            var test = mongo.GetDatabase("foo");
 
            // insert ContainerV1 record (without array member)
            var coll1 = test.GetCollection<ContainerV1>("bar");
            var item = new ContainerV1 { Id = Guid.NewGuid().ToString("N"), A = true };
            coll1.InsertOneAsync(item).GetAwaiter().GetResult();
 
            // get from same collection after model changes.
            var coll2 = test.GetCollection<ContainerV2>("bar");
            var results = coll2.AsQueryable().Where(i => i.A).Select(i => new { Blah = i.Blah }).ToArray();

The exception: "No matching creator found." (BsonSerializationException), from BsonClassMapSerializer.ChooseBestCreator.

The reason:

Here's the server command:

{ "aggregate" : "bar", "pipeline" : [{ "$match" : { "A" : true } }, { "$project" : { "Blah" : "$Blah", "_id" : 0 } }], "cursor" : { } }


The response:

{ "cursor" : { "id" : NumberLong(0), "ns" : "foo.bar", "firstBatch" : [{ }] }, "ok" : 1.0 }

Basically, the array part of the projection comes back as an empty document when it doesn't exist, as would be the case when the member is new. BsonClassMapSerializer.DeserializeClass() doesn't populate any values from it of course, and no missing values are populated because the array member is marked IsReadOnly.

In ChooseBestCreator, the MostArgumentsCreatorSelector() looks for an appropriate creator, but with no values it can't determine one.

I'm not 100% on what the bug is, but perhaps - looking at DeserializeClass() - MemberMap.IsReadOnly for the array property of the projection should not be null, or should not be checked for null when dealing with missing values?

(Marking as minor since I'm not sure if the deserialisation code should be expected to deal with whatever crazy cases might be thrown at it like this)



 Comments   
Comment by Robert Stam [ 19/Jun/19 ]

The issue is not so much that an array was added to the data model, but that the automatically created serializer for an anonymous type does not allow missing elements.

I've created a new linked ticket for the underlying anonymous type serialization issue.

https://jira.mongodb.org/browse/CSHARP-2643

Comment by Kevin Fairs [ 04/Apr/19 ]

Just hit this in v.2.8.0. Do these ever get fixed?

Comment by Andrey Kalinin [ 18/Dec/17 ]

I have this problem too.

_Repository.AsQueryable().Select(x => new { x.Identifier, x.Location, x.Id, x.IsFree }).ToList()

This code throw BsonSerializationException after add new boolean field "IsFree".
Please resolve this problem, or say how to avoid this exception.

Comment by Micha? D?biec [ 27/Oct/16 ]

I have also expirienced this issue in following case:

My class has Description property:

[BsonIgnoreIfDefault]
[String()]
public string Description { get; set; }

Due to BsonIgnoreIfDefault attribute null values are not stored in database (this is nice). Description property is not present in some documents.

{
    "DocumentId" : ObjectId("5800c7dd1a2701121c265ef5"),
    "Name" : "Test document 1",
    "Description" : "Some description",
    "Owner" : "Owner",
    "CreateDate" : ISODate("2016-10-14T11:56:13.776Z")
}
{
    "DocumentId" : ObjectId("5800c2ea1a2701121c265dc2"),
    "Name" : "Test document 2",
    "Owner" : "Owner",
    "CreateDate" : ISODate("2016-10-14T11:35:06.978Z")
}

Than i run the query with projection:

var result = collection.AsQueryable().Select(o => new {o.DocumentId, o.Name, o.Description});

Iterating over result crashes with: MongoDB.Bson.BsonSerializationException: No matching creator found.

I can probably do not use BsonIgnoreIfDefault attribute in my model and reinsert/update the data, so the problem will be temporairly solved, but it will come back when the model grows (new properties will be added in future).

Plase fix it somehow.

Comment by Alexey [ 03/Jun/16 ]

We faced this bug too, it prevents us from using dynamically composed .Select on some dictionary styled fields. Can this please be fixed?

Comment by Kieren Johnstone [ 28/Jun/15 ]

It looks like the creator isn't matched because there's no default value for the member of the anonymous type. No value and no default = it thinks it can't proceed.

I have a solution - would like to check its validity:

BsonCreatorMap.Freeze() - populates the default value for that map. Removing the check to see if the default value has been specified -

if (memberMap.IsDefaultValueSpecified)

- fixes this issue for me.

The

.DefaultValue

member access still works: GetDefaultValue() on the member map will return null for reference types, which is correct. I don't think the

IsDefaultValueSpecified

check is required here? If that's the case, I can set up a PR?

Generated at Wed Feb 07 21:39:18 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.