I use this code to retrieve some documents:
var client = new MongoClient(connectionString);
var database = client.GetDatabase(databaseName);
var collection = database.GetCollection<BsonDocument>(collectionName);
var json = "{created: {$gte: ISODate(\"2018-12-20T00:00:00.000Z\"), $lt:
ISODate(\"2018-12-21T00:00:00.000Z\")}}";
BsonDocument query = MongoDB.Bson.Serialization.BsonSerializer.Deserialize<BsonDocument>(json);
var documents = collection.Find(query).Limit(10);
The results look like this:
{ "_id" : CSUUID("75c5634c-b64b-4484-81f5-5b213228e272"), ..., "created" : ISODate("2018-12-20T23:59:13.375Z") }
I'm having trouble retrieving this same document when trying to filter on _id. Here are the filters I've tried (using the same code as above) and have not been able to retrieve the document:
var json = "{ \"_id\" : \"75c5634c-b64b-4484-81f5-5b213228e272\" }";
var json = "{ \"_id\" : CSUUID(\"75c5634c-b64b-4484-81f5-5b213228e272\") }";
var json = "{ \"_id\" : new BinData(4, \"TGPFdUu2hESB9VshMijicg==\") }";
var json = "{ \"_id\" : BinData(4, \"TGPFdUu2hESB9VshMijicg==\") }";
var json = "{ \"_id\" : new BinData(3, \"TGPFdUu2hESB9VshMijicg==\") }";
var json = "{ \"_id\" : BinData(3, \"TGPFdUu2hESB9VshMijicg==\") }";
var json = "{ \"_id\" : { $eq: \"TGPFdUu2hESB9VshMijicg==\" } }";
var json = "{ \"_id\" : { $binary: \"TGPFdUu2hESB9VshMijicg==\", $type: 4 } }";
var json = "{ \"_id\" : { $binary: \"TGPFdUu2hESB9VshMijicg==\", $type: 3 } }";
Note, TGPFdUu2hESB9VshMijicg==
was retrieved by getting a base 64 encoded string from the guid like this:
Convert.ToBase64String((new Guid("75c5634c-b64b-4484-81f5-5b213228e272")).ToByteArray())
None of the queries throw any exceptions, but they return no documents.
The MongoDb C# driver does a lot of work trying to keep you away of the Json (Bson) representation. To oversimplify, you have 3 ways of working with MongoDb with C#
BsonDocument
class.And of course, a combination of the 3, which makes things much worse :-)
So, in your case, here is how you would do the BsonDocument way (w/o any JSON):
var client = new MongoClient(myConnectionString);
var db = client.GetDatabase("myDb");
var guid = Guid.NewGuid();
// create an untyped document
var doc = new BsonDocument { { "_id", guid } };
var coll = db.GetCollection<BsonDocument>("myColl");
coll.InsertOne(doc);
// Builders<T> is central to help you build all sorts of mongodb JSON jargon (filters, sort, projections, etc.)
// instead of building it by yourself
var filter = Builders<BsonDocument>.Filter.Eq(new StringFieldDefinition<BsonDocument, Guid>("_id"), guid);
var foundDoc = coll.Find(filter).FirstOrDefault();
Console.WriteLine(foundDoc["_id"]);
And here is how you could do the typed-document way (w/o any JSON and w/o any BsonDocument):
var client = new MongoClient(myConnectionString);
var db = client.GetDatabase("myDb");
var guid = Guid.NewGuid();
// create a class
var doc = new MyDoc { Id = guid };
var coll = db.GetCollection<MyDoc>("myColl");
coll.InsertOne(doc);
// we use a type that correspond to our busines layer/logic
// that's the easier way because you can use Linq syntax so we're far from JSON and document issues
// plus it's super readable in C#
var foundDoc = coll.Find(d => d.Id == guid).FirstOrDefault();
Console.WriteLine(foundDoc.Id);
...
// the typed-document (class)
class MyDoc
{
[BsonId]
public Guid Id { get; set; }
... other properties...
}
As you see, the last way is much simpler, but we can't always use it.
BTW, it's sad that the driver doesn't allow to derive MyDoc
from BsonDocument
, because we would truly have best of both worlds (it compiles but throws.... if MongoDb C# devs read this...)
Now, concerning guids, you'll note the Console.WriteLine(foundDoc["_id"])
displays UuidLegacy:0x87fa981983de774b998868046e257b19
because MongoDb has a legacy history with guids.
As you found out, you can change BsonDefaults.GuidRepresentation
. By default it's CSharpLegacy
.
Here is a list of prefixes used when guids are displayed as strings throughout code (client or server):
The 2 and 3 approaches also shield you from these "internal" MongoDb issues. If you use these, you don't have to change BsonDefaults.GuidRepresentation
.
So my advise is try to stay away from Json when programming MongoDb with C#.
Adding this before even creating MongoClient() resolved the issue for me:
BsonDefaults.GuidRepresentation = GuidRepresentation.Standard;
It looks like on the C# side the MongoDB driver was interpreting it as a UUID with a binary subtype of 3. However, the documents saved in the collection had a binary subtype of 4.
Also, after this change the document retrieved shows "UUID()" rather than "CSUUID()":
{ "_id" : UUID("75c5634c-b64b-4484-81f5-5b213228e272"), ..., "created" : ISODate("2018-12-20T23:59:13.375Z") }
After more time than I'd care to admit searching the web and testing many theories, the breakthrough came from skimming through this article: https://www.codeproject.com/Articles/987203/%2FArticles%2F987203%2FBest-Practices-for-GUID-data-in-MongoDB
Excerpt from that link:
MongoDB drivers usually store UUIDs as Binary fields with the legacy 0x03 subtype assigned by default. This configuration can be changed:
C#:
You can override the driver’s default settings and configure it to use the Binary 0x04 subtype by modifying the value of BsonDefaults.GuidRepresentation:
BsonDefaults.GuidRepresentation = GuidRepresentation.Standard;
You can also modify GuidRepresentation at the server, database and collection level.
EDIT:
This is what I ended up using for the json filter:
var json = "{ \"_id\" : UUID(\"75c5634c-b64b-4484-81f5-5b213228e272\") }";
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With