Hi!
I recently came across some odd behaviour during an upsert while using FindOneAndUpdateAsync.
For some reason, the BsonType of a string property with null value is being converted from BsonNull to BsonString and, consequently, the property is being saved with the value of BsonNull, instead of null!
The builders we provide for building filter definitions or update operations have both a type-safe interface such as Builders<T>.Update.Set(t => t.Url, "sample.org")) and string-based interface such as Builders<T>.Update.Set("Url", "sample.org")). The type-safe interface provides compile-time safety while the string-based interface doesn’t which allows for weird things to happen at runtime if the user didn’t make sure their field type and value type match.
In the snippet you provided, you converted the document to a BsonDocument which will convert the null value of the Url field to a BsonNull as that is how nulls are represented in a BsonDocument.
In this section above, you are using the string-based interface for the update definition where for the case of the Url field, it’ll be Builders<T>.Update.Set("Url", BsonNull)). The type of Url for T is string but we are trying to assign a BsonNull to it. Now here is where the driver is doing something wrong. It should ideally throw an exception as there is no conversion from BsonNull to string but it instead uses the string representation of BsonNull.
We’ll probably make a ticket to have the driver throw in such situations. In your snippet I don’t know why you are converting your document to a BsonDocument, but you should avoid converting it and use the type-safe interface with the builders. In general, I recommend always using the type-safe interface when working with POCOs (Plain Old CLR Objects). The string-based interface should really only be used when working with BsonDocuments.