[CSHARP-4049] Aggregate().Project() with strongly typed class doesn't use ElementName Created: 11/Feb/22  Updated: 28/Oct/23  Resolved: 04/Jul/22

Status: Closed
Project: C# Driver
Component/s: Builders, LINQ3
Affects Version/s: 2.14.0
Fix Version/s: 2.17.0

Type: Bug Priority: Unknown
Reporter: Antonios Daskos Assignee: Robert Stam
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Epic Link: CSHARP-3615

 Description   

Tested with version 2.14.1 and LINQ provider V2.

Given the class:

 

public class TestClass
{
  [BsonElement("_p")]
  public string Property { get; set; }
}

 

This query:

 

var queryable = collection.AsQueryable() .Select(x => new TestClass{ Property = x.Property.ToUpper() });

 

is handled correctly and produces the following MQL request:

 

{ 
  "aggregate": "coll", 
  "pipeline": [{ 
    "$project": { 
      "_p": { "$toUpper": "$_p" }, 
      "_id": 0 } } ], 
  "cursor": {} }

 

While this query: 

collection.Aggregate()
  .Project(
    Builders<TestClass>.Projection
     .Expression(x => new TestClass { Property = x.Property.ToUpper() }));

give the wrong query (still refers to attribute as Property:

 

{ 
  "aggregate": "coll", 
  "pipeline": [{ 
    "$project": { 
      "_p": { "$toUpper": "$_p" }, 
      "_id": 0 } } ], 
  "cursor": {} }   

 

 

 

 



 Comments   
Comment by James Kovacs [ 04/Jul/22 ]

This issue has been fixed in the new LINQ provider (known as LINQ3), which was introduced in the 2.14 release.

Configure your MongoClientSettings to use LinqProvider.V3 if you want to use this functionality.

To configure a client to use the LINQ3 provider use code like the following

var connectionString = "mongodb://localhost";
var clientSettings = MongoClientSettings.FromConnectionString(connectionString);
clientSettings.LinqProvider = LinqProvider.V3;
var client = new MongoClient(clientSettings);

Comment by Githook User [ 04/Jul/22 ]

Author:

{'name': 'Robert Stam', 'email': 'robert@robertstam.org', 'username': 'rstam'}

Message: CSHARP-4049: Verify that issue is not present in LINQ3. (#841)
Branch: master
https://github.com/mongodb/mongo-csharp-driver/commit/cc238140b5a6dea182baeb92115dc60567af3834

Comment by Robert Stam [ 02/Jul/22 ]

As Dmitry pointed out Builders<TDocument>.Project.Expression(x => ...) only works with the Find family of methods.

To specify a projection in Aggregate simply use the appropriate overload of the Project method that takes an expression directly.

Replace:

aggregate.Project(Builders<T>.Projection.Expression(x => ...)) 

with:

aggregate.Project(x => ...)

which in addition has the benefit of being more concise.

Comment by Antonios Daskos [ 11/Feb/22 ]

Looks good to me. Thanks for expanding on the details. 

Comment by Dmitry Lukyanov (Inactive) [ 11/Feb/22 ]

Hey a.daskos@signalocean.com , please correct me if I'm wrong, but this is more detailed description that we discussed in the community forum:

This query:

var queryable = collection.AsQueryable() .Select(x => new TestClass{ Property = x.Property.ToUpper() });

is correctly handled only in LINQ3 and produces the following query:

{
        "aggregate": "coll",
        "pipeline": [{
                "$project": {
                    "_p": {
                        "$toUpper": "$_p"
                    },
                    "_id": 0
                }
            }
        ],
        "cursor": {}
}

LINQ2 doesn't consider BsonElement attribute and generates the following MQL:

{
        "aggregate": "coll",
        "pipeline": [{
                "$project": {
                    "Property": {
                        "$toUpper": "$_p"
                    },
                    "_id": 0
                }
            }
        ],
        "cursor": {}
}

However when Builder.Projection.Expression is involved, for example:

collection
    .Aggregate()
    .Project(
        Builders<TestClass>
        .Projection
        .Expression(x => new TestClass { Property = x.Property.ToUpper()})
    );

 both LINQ2 and LINQ3 generates the same wrong output:

{
        "aggregate": "coll",
        "pipeline": [{
                "$project": {
                    "_p": 1,
                    "_id": 0
                }
            }
        ],
        "cursor": {}
}

that also leads to the exception during deserialization of the server response.

NOTE:

if we use Aggregate.Project method explicitly like:

            collection
                .Aggregate()
                .Project(
                    x => new TestClass { Property = x.Property.ToUpper() }
                ).ToList();

the behavior is the same as with AsQueryable (works only in LINQ3)

Comment by Antonios Daskos [ 11/Feb/22 ]

Wrong copy-paste but I cannot edit.

The second case produces:

{ 
  "aggregate": "coll", 
  "pipeline": [{ 
    "$project": { "Property": { "$toUpper": "$_p" }, 
    "_id": 0 } } ], 
  "cursor": {} }  

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