[CSHARP-4766] Setting a sub-field on update throws a BsonSerializationException Created: 20/Aug/23  Updated: 05/Sep/23  Resolved: 05/Sep/23

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

Type: Bug Priority: Unknown
Reporter: Alex Piskarev Assignee: Oleksandr Poliakov
Resolution: Won't Fix Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Documentation Changes Summary:

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?


 Description   

The following should reproduce a MongoDB.Bson.BsonSerializationException on update.

// — Model:

public class Test

{     [BsonId]     public object Id \{ get; set; }

= ObjectId.GenerateNewId();

    public string Name { get; set; }

    public byte[] Data { get; set; }

    public BsonDocument Doc { get; set; }

    public static IMongoCollection<Test> GetCollection()
   

{         // Get DB         var db = TestLibrary.Db.MongoDb;         return db.GetCollection<Test>("Tests");     }

}

// — Code:

var t = new Test();

t.Name = "TestName";

t.Doc = new BsonDocument();

t.Doc["Name"] = "TestDocName";

await c.InsertOneAsync(t, cancellationToken: cancellationToken);

string? nullString = null;

await c.UpdateOneAsync(
    d => d.Id == t.Id,
    Builders<Test>.Update
        .Set(d => d.Data, null)
        .Set("Name", nullString)
        .Set("Doc.Name", nullString),      // <-- Source of error
    cancellationToken: cancellationToken);

MongoDB.Bson.BsonSerializationException: 'C# null values of type 'BsonValue' cannot be serialized using a serializer of type 'BsonValueSerializer'.'



 Comments   
Comment by Oleksandr Poliakov [ 05/Sep/23 ]

Hi alex.covecube!

After deep investigation of the reported problem we decided not to change current behavior to keep consistent way of working with BsonDocument class. Whenever you are interacting with BsonDocument it should be used together with BsonValues. For example the following code will throw as well:

 

var document = new BsonDocument();
document["ab"] = null; 

On other hand the following code is working because there is implicit conversion from the C# string to BsonString (in fact C# silently converting string to BsonString so  BsonDocument still working with BsonValue):

 

 

var document = new BsonDocument();
document["ab"] = "value"; 

More over if we would support some way to set null as a BsonDocument - what the expected way to read the document back from the database? Should it contains null or BsonNull value?

 

I hope this explains our motivation to keep the existing driver's behavior.

For your particular example I can suggest you either create a strongly typed model (replace property of BsonDocument with some POCO) or use the following code snippet whenever you are trying to set the value of BsonDocument:

 

 

stringVar ?? (object)BsonNull.Value;  

inside the Set operation it would looks like:

 

 

 updateOperation.Set("Doc.Name", stringVar ?? (object)BsonNull.Value), 

 

I'll close the ticket, but feel free to add more comments if you will need additional assistance or questions.

 

Thanks,

Oleksandr Poliakov

Comment by Alex Piskarev [ 20/Aug/23 ]

This fixes it:

await c.UpdateOneAsync(
                d => d.Id == t.Id,
                Builders<Test>.Update
                    .Set(d => d.Data, null)
                    .Set("Name", nullString)
                    .Set("Doc.Name", nullString ?? (object)BsonNull.Value),
                cancellationToken: cancellationToken);

Comment by PM Bot [ 20/Aug/23 ]

Hi alex.covecube, thank you for reporting this issue! The team will look into it and get back to you soon.

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