[CSHARP-932] Querying for BsonObjectId == null causes errors Created: 19/Mar/14  Updated: 02/Apr/16  Resolved: 22/Apr/14

Status: Closed
Project: C# Driver
Component/s: Serialization
Affects Version/s: 1.8.3
Fix Version/s: 1.9.1

Type: Bug Priority: Major - P3
Reporter: Einar Egilsson Assignee: Robert Stam
Resolution: Done Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Related
related to CSHARP-863 How should BsonValues be serialized f... Closed

 Description   

We are trying to upgrade from version 1.7.1 of the driver to 1.8.3. One of the problems we have run into is that BsonObjectId can no longer be saved as null in the database, instead it's saved as

{ _csharpnull : true }

.

I don't understand why this change was made. BsonObjectId is a class, and can be null, so why shouldn't it be represented as null in the database?

One example of things that break now is:

public class TestClass
{
public BsonObjectId SomeId

{ get; set; }

}

[Test]
public void TestNullBsonObjectId()
{

_collection.Insert(new TestClass

{ SomeId = null }

);

//Breaks completely because BsonObjectId null can't be serialized
var objects = _collection.AsQueryable<TestClass>().Where(doc => doc.SomeId == null).ToList();
}

So the driver is perfectly happy to save documents with a BsonObjectId property that is null to the database, but if you try to query them again to get the ones where the id is null that breaks completely. I could create a query to explicitly check for SomeId._csharpnull = True, but that just seems weirdly csharp specific.

The LINQ query thing above is clearly a bug, although I don't see how it can be fixed. But my main question is, what would you recommend? We have millions of records that can have null values in fields of type BsonObjectId. Should we change them to a Nullable<ObjectId> instead in our CSharp code? Or something else? Create our own Id type that is not a BsonValue?

I tried to force the 1.8 driver to behave like 1.7.1 but since it's not possible to register a custom serializer for BsonObjectId, and the serialization writes the {_csharpnull:true} thing before checking the member map I wasn't able to change it back.

Thanks in advance,
Einar



 Comments   
Comment by Githook User [ 23/Apr/14 ]

Author:

{u'name': u'rstam', u'email': u'robert@10gen.com'}

Message: CSHARP-932: Fix queries comparing BsonValue properties to C# null.
Branch: master
https://github.com/mongodb/mongo-csharp-driver/commit/75c0281226b4c89bcddc29519fb9f00b8177ebd6

Comment by Githook User [ 22/Apr/14 ]

Author:

{u'name': u'rstam', u'email': u'robert@10gen.com'}

Message: CSHARP-932: Fix queries comparing BsonValue properties to C# null.
Branch: v1.x
https://github.com/mongodb/mongo-csharp-driver/commit/fd47b44f580c962df0ee2bb9b99a58868b8a94cb

Comment by Robert Stam [ 20/Mar/14 ]

I'm linking this ticket to CSHARP-863 which describes the serialization part of this issue.

I'm not calling it a duplicate because this ticket is also about the Query builder failing.

Comment by Robert Stam [ 19/Mar/14 ]

The LINQ issue can actually be reproduced using the Query builder alone:

public class C
{
    public BsonObjectId V;
}
 
var query = Query<C>.Where(c => c.V == null); // throws an exception
var json = query.ToJson();

Comment by Einar Egilsson [ 19/Mar/14 ]

Yes, that's a fair point. Anyway, many thanks for the quick responses.

Comment by Robert Stam [ 19/Mar/14 ]

I see... sorry I misunderstood.

For better or for worse, we've put a lot of emphasis on 100% fidelity in our serialization. We want to make sure that when you deserialize an object you get back exactly what you started with.

Comment by Einar Egilsson [ 19/Mar/14 ]

Yes, I realize that. I just think that it would be an acceptable special case if it worked like:

var o = new C();
o.V = null; //CSharp null
 
var bytes = o.ToBson();
//Serializes to { V: null }
 
var backFromBson = BsonSerializer.Deserilize<C>(bytes);
backFromBson.V is BsonNull.Value; //true

I.e. I think it would be acceptable that a csharp null will come back from the database as BsonNull.Value if your field is of type BsonValue. I would guess that having properties of type BsonValue on your objects is less common than having properties of type BsonObjectId.

Anyway, that was just to explain what I meant. I realize that it's not likely to be changed back to 1.7.1 behavior at this point.

Comment by Robert Stam [ 19/Mar/14 ]

Just to clarify, if you deserialize:

{ V : null }

the value of V is BsonNull.Value (not C# null), assuming your property is declared as a BsonValue (and not some subclass).

That's why we need an alternate representation for C# null values.

Comment by Einar Egilsson [ 19/Mar/14 ]

Thanks for the answer.

I actually think it would have been better if whenever you had a property of type BsonValue and you deserialized

{ Prop : null }

from the database it would just become BsonNull.Value. It would be a weird special case, yes, but I think it's even weirder now that any property of type BsonObjectId, BsonDocument and other BsonValue derived type can be null in csharp, but can't be compared to null in a query, and has a very programming language specific representation in the database.

But everything is a tradeoff I guess. We'll switch to ObjectId? or a custom Id class that doesn't inherit from BsonValue instead.

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