Hi, Nick,
Thank you for your patience and thank you for filing CSHARP-4507. I was able to reproduce the issue with the provided code along with the additional detail that it only fails on MongoDB 4.2 and earlier. I will explain the problem and then some potential workarounds while we work on a fix.
To answer your first question, MongoDB 4.2 is still a supported server version though it will reach end-of-life in April 2023. See our support policy for full details.
Now let’s discuss the root cause of this issue. The problem stems from the renaming of the fields in your LINQ query. The following code will display the MQL sent to the server. The same MQL is sent to MongoDB regardless of the server version.
var query = coll.Find(filter).Project(m => new DeleteInfoWrapper(m.ObjectId, m.GridFsObjectId, m.AttachmentGridFsObjectId));
Console.WriteLine(query);
The resulting MQL is:
find({ "_id" : { "$in" : [ObjectId("63d856bb965b1cf474a00a6b"), ObjectId("63d856ba965b1cf474a00a68")] } }, { "ObjectId" : "$_id", "GridFsObjectId" : "$grid", "AttachmentGridFsObjectId" : "$agrd", "_id" : 0 })
The second argument to the find
command is the projection. Note the syntax "CsharpFieldName": "$databaseFieldName"
. For example "GridFsObjectId": "$grid"
. This is where the problem lies. In MongoDB 4.4 and later, you could use this $fieldName
syntax to rename fields in projections - whether those projections were part of a find
operation or an aggregation pipeline.
However in MongoDB 4.2 and earlier, you could only use this syntax in aggregation pipelines, but not in find projections. Find projections only allowed the older, simpler syntax of fieldName: 1
to include a field. This is further complicated by the fact that for backwards compatibility, fieldName: VALUE
where VALUE was truthy in the JavaScript sense. This meant that 0, false, null, and undefined are interpreted as false and pretty much everything else was interpreted as true. Thus MongoDB 4.2 (using find) sees "GridFsObjectId": "$grid"
as "GridFsObjectId": true
. Since there is no field in the document named GridFsObjectId
, the field is omitted leading to the observed behaviour.
In MongoDB 4.4, we enhanced the find projection to use the same syntax as the aggregation pipeline. Thus MongoDB 4.4 sees "GridFsObjectId": "$grid"
as "GridFsObjectId": "$grid"
and correctly renames the field grid
(in the database) to GridFsObjectId
in the returned document.
Possible workarounds for this issue (in no particular order):
- Upgrade to MongoDB 4.4 or later.
- Continue using LINQ2.
- Refactor your Fluent Find queries to Fluent Aggregate.
The first two should be self-explanatory. I will note that LINQ3 has greatly enhanced capabilities including support for new aggregation features. LINQ2 will be deprecated in an upcoming version and removed in the 3.0.0 driver. We have not announced a public timeline for the 3.0.0 driver.
Refactoring to use Fluent Aggregation is probably the most straightforward. You can use the same FilterDefinition<>
and ProjectionDefinition<,>
as you are currently using with Find
/Project
. Rather than coll.Find(filter).Project(projection)
you would instead use coll.Aggregate().Match(filter).Project(projection)
.
var query = coll.Aggregate().Match(filter).Project(m => new DeleteInfoWrapper(m.ObjectId, m.GridFsObjectId, m.AttachmentGridFsObjectId));
Console.WriteLine(query);
The resulting MQL produces an aggregation pipeline rather than a find
command, but the query results are the same:
aggregate([{ "$match" : { "_id" : { "$in" : [ObjectId("63d856bb965b1cf474a00a6b"), ObjectId("63d856ba965b1cf474a00a68")] } } }, { "$project" : { "ObjectId" : "$_id", "GridFsObjectId" : "$grid", "AttachmentGridFsObjectId" : "$agrd", "_id" : 0 } }])
This aggregation pipeline - including the projection - will work on even very old server versions. I tested it on MongoDB 3.6 and it produced the correct results.
Please follow CSHARP-4507 for the fix. Thank you again for reporting it. Let us know if you have any additional questions.
Sincerely,
James