[CSHARP-4632] The memberinfo argument must be for class {ChildTestDto}, but was for {ParentTestDto}} Created: 02/May/23  Updated: 28/Oct/23  Resolved: 30/May/23

Status: Closed
Project: C# Driver
Component/s: None
Affects Version/s: None
Fix Version/s: 2.20.0

Type: Bug Priority: Unknown
Reporter: Arne Schoonvliet Assignee: Oleksandr Poliakov
Resolution: Fixed Votes: 0
Labels: LINQ3, linq3, triage
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Attachments: PNG File Screenshot 2023-05-02 134703.png    
Issue Links:
Gantt Dependency
has to be done after CSHARP-4656 Simplify A : "$A" to A : 1 only on find Closed
has to be done after CSHARP-4550 NewExpression and MemberInitExpressio... Closed
Backwards Compatibility: Fully Compatible
Documentation Changes: Not Needed
Documentation Changes Summary:

1. What would you like to communicate to the user about this feature?
2. Would you like the user to see examples of the syntax and/or executable code and its output?
3. Which versions of the driver/connector does this apply to?


 Description   

Summary

Projecting to a POCO using constructor that is inheriting from a base POCO fails to project using the LinqProvider V3 but works fine using the LinqProvider V2. 

Please provide the version of the driver. If applicable, please provide the MongoDB server version and topology (standalone, replica set, or sharded cluster).

driver: 2.19.1

How to Reproduce

Test code: 

public class InheritanceTest
{
    [Fact]
    public async Task Test()
    {
        BsonDefaults.GuidRepresentationMode = GuidRepresentationMode.V3;
        BsonSerializer.RegisterSerializer(new GuidSerializer(GuidRepresentation.Standard));
        var mongoClient = new MongoClient(MongoClientSettings.FromConnectionString("mongodb://intigriti:!ntIgrit!@localhost:27017/?replicaSet=rs1"));
        var database = mongoClient.GetDatabase("testdatabase");
        var collection = database.GetCollection<TestDocument>("testdocuments");
        var id = Guid.NewGuid();
        await collection.InsertOneAsync(new TestDocument { Id = id, Name = "test" });
        var dto = await collection
            .Find(x => x.Id == id)
            .Project(x => new ChildTestDto(x.Id, x.Name))
            .FirstAsync();
    }
 
    public class TestDocument
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
    }
 
    public class ParentTestDto
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public ParentTestDto(Guid id, string name)
        {
            Id = id;
            Name = name;
        }
    }
 
    public class ChildTestDto : ParentTestDto
    {
        public ChildTestDto(Guid id, string name) : base(id, name)
        {
            
        }
    }
} 

Additional Background

We are trying to upgrade to the latest driver version but are hitting multiple roadblocks. It feels like the LinqProvider V3 isn't production ready. Documentation with some guidelines for migration would be a nice addition. Like the V2 provider has a fall-back to in memory projection when the expression isn't supported while V3 just crashes hard.



 Comments   
Comment by Githook User [ 30/May/23 ]

Author:

{'name': 'Oleksandr Poliakov', 'email': '31327136+sanych-sun@users.noreply.github.com', 'username': 'sanych-sun'}

Message: CSHARP-4632: Consolidate new and init expression translation (#1094)
Branch: master
https://github.com/mongodb/mongo-csharp-driver/commit/f958b939fd42551106dd8229dc9343544a7bac5e

Comment by James Kovacs [ 03/May/23 ]

Hi, arne@intigriti.com,

We are able to reproduce the reported issue. I will include a simplified repro below. The root cause of the problem is that the driver assumes that constructor arguments match properties defined on the class, not on a base class. We are working on a fix.

In the meantime you can work around the issue by continuing to use LINQ2 or switching to property initializer syntax with LINQ3. This would require defining a parameterless constructor for your DTO.

.Project(x => new ChildTestDto{Id = x.Id, Name = x.Name}) 

We apologize that the LINQ3 transition has not been a smooth one for you. When implementing LINQ3, we ensured that all existing LINQ2 tests pass as well as writing additional tests for new features. Unfortunately existing tests obviously didn't cover all possible scenarios such as yours.

We first introduced LINQ3 in the 2.14 driver over a year ago as an opt-in improvement. We have been resolving reported issues aggressively to remediate problems discovered in LINQ3. We knew that eventually we would have to make LINQ3 the default (rather than opt-in) and this would surface additional LINQ3 issues given the myriad ways that LINQ can be used. That change to make LINQ3 the default provider happened in the 2.19. We should have documented the known changes when moving from LINQ2 to LINQ3. You can follow CSHARP-4618 for this request.

The LINQ2 provider will continue to be available in the 2.X driver series. Support for LINQ2 will not be removed until the 3.0.0 driver, which is still awhile away.

The team is committed to resolving outstanding issues in LINQ3 for queries that worked in LINQ2. Your feedback is invaluable in reporting real world use cases that are not yet covered by our test suite. I encourage you to report any additional problems that you find during your migration.

Sincerely,

James

 

The following test case passes in LINQ2 but fails in LINQ3:

using FluentAssertions;
using MongoDB.Bson;
using MongoDB.Driver.Linq;
using MongoDB.TestHelpers.XunitExtensions;
using Xunit;namespace MongoDB.Driver.Tests.Linq.Linq3ImplementationTests.Jira
{
    public class CSharp4632Tests : Linq3IntegrationTest
    {
        [Theory]
        [ParameterAttributeData]
        public void Projection_into_chained_child_ctor_should_work(
            [Values(LinqProvider.V2, LinqProvider.V3)] LinqProvider linqProvider)
        {
            var collection = GetCollection<TestDocument>("test", linqProvider);
            var doc = new TestDocument { Name = "test" };
            collection.InsertOne(doc);
            var dto = collection
                .Find(x => x.Id == doc.Id)
                .Project(x => new ChildTestDto(x.Id, x.Name))
                .First();
            dto.Id.Should().Be(doc.Id);
            dto.Name.Should().Be(doc.Name);
        }        class TestDocument
        {
            public ObjectId Id { get; set; }
            public string Name { get; set; }
        }        class ParentTestDto
        {
            public ObjectId Id { get; set; }
            public string Name { get; set; }            public ParentTestDto()
            {            }
            public ParentTestDto(ObjectId id, string name)
            {
                Id = id;
                Name = name;
            }
        }        class ChildTestDto : ParentTestDto
        {
            public ChildTestDto()
            {            }
            public ChildTestDto(ObjectId id, string name) : base(id, name)
            {
            }
        }
    }
} 

 

Comment by Service Account: DBX TPM [ 02/May/23 ]

Hi arne@intigriti.com, thank you for reporting this issue! The team will look into it and get back to you soon.

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