[CSHARP-4653] LINQ3 provider: MongoDB.Driver.Linq.ExpressionNotSupportedException: Expression not supported: Convert(p) because conversion to "Subclass" is not supported. Created: 15/May/23  Updated: 28/Oct/23  Resolved: 31/May/23

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

Type: Bug Priority: Minor - P4
Reporter: Roberto Pérez Assignee: Oleksandr Poliakov
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

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

The latest MongoDB.Driver for C# switched the default LINQ Provider from 2 to 3. After trying to upgrade and use version 3 the code that used to work in V2 fails in V3.

Server versions: 5.0.13 Windows and 5.0.10 Ubuntu

How to Reproduce

    internal class Program5
    {
        public class Preference
        {
            public Guid Id { get; set; }
            public PreferenceType Type { get; set; }
        }        public enum PreferenceType
        {
            EmailAlerts,
            Another
        }        public interface IEmailAlertsPreference
        {
            bool Enabled { get; set; }
            string Description { get; set; }
        }        public class EmailAlertsPreference : Preference, IEmailAlertsPreference
        {
            public EmailAlertsPreference()
            {
                Type = PreferenceType.EmailAlerts;
            }            public bool Enabled { get; set; }
            public string Description { get; set; }
        }        internal static void Main2(string[] args)
        {
            foreach (var linqProvider in new []
                     {
                         LinqProvider.V2, 
                         LinqProvider.V3
                     })
            {
                var builder = new MongoUrlBuilder
                {
                    DatabaseName = "Sample",
                    Server = new MongoServerAddress("localhost", 27017)
                };
                var mongoClientSettings = MongoClientSettings.FromUrl(builder.ToMongoUrl());
                mongoClientSettings.LinqProvider = linqProvider;
                var mongoClient = new MongoClient(mongoClientSettings);
                var mongoDatabase = mongoClient.GetDatabase(builder.DatabaseName);                var preferencesCollection = mongoDatabase.GetCollection<Preference>("Preferences");
                preferencesCollection.DeleteMany(c => true);
                preferencesCollection.InsertOne(new EmailAlertsPreference
                {
                    Id = Guid.NewGuid(),
                    Enabled = true,
                    Description = $"{linqProvider} - Description"
                });                var queryableWithoutProjection = preferencesCollection.AsQueryable()
                    .Where(p => p.Type == PreferenceType.EmailAlerts && ((EmailAlertsPreference)p).Enabled);                Console.WriteLine("*************************");
                Console.WriteLine("Without custom projection");
                Console.WriteLine("*************************");
                foreach (var emailAlertsPreference in queryableWithoutProjection.ToList().Cast<EmailAlertsPreference>())
                {
                    Console.WriteLine(emailAlertsPreference.Enabled);
                    Console.WriteLine(emailAlertsPreference.Description);
                }
                Console.WriteLine("*************************");
                Console.WriteLine();                Console.WriteLine("*************************");
                Console.WriteLine("With custom projection");
                Console.WriteLine("*************************");
                Expression<Func<Preference, EmailAlertsPreference>> projection = p => new EmailAlertsPreference
                {
                    Enabled = ((EmailAlertsPreference)p).Enabled,
                    Description = ((EmailAlertsPreference)p).Description
                };
                foreach (var emailAlertsPreference in queryableWithoutProjection.Select(projection).ToList())
                {
                    Console.WriteLine(emailAlertsPreference.Enabled);
                    Console.WriteLine(emailAlertsPreference.Description);
                }
                Console.WriteLine("*************************");
                Console.WriteLine();
                Console.WriteLine();
            }
        }
    } 

Execution output:

*************************
Without custom projection
*************************
True
V2 - Description
**************************************************
With custom projection
*************************
True
V2 - Description
*************************
*************************
Without custom projection
*************************
True
V3 - Description
**************************************************
With custom projection
*************************Unhandled Exception: MongoDB.Driver.Linq.ExpressionNotSupportedException: Expression not supported: Convert(p) because conversion to Sample.Program5+EmailAlertsPreference is not supported.
   at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.ConvertExpressionToAggregationExpressionTranslator.Translate(TranslationContext context, UnaryExpression expression)
   at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.ExpressionToAggregationExpressionTranslator.Translate(TranslationContext context, Expression expression)
   at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MemberExpressionToAggregationExpressionTranslator.Translate(TranslationContext context, MemberExpression expression)
   at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.ExpressionToAggregationExpressionTranslator.Translate(TranslationContext context, Expression expression)
   at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MemberInitExpressionToAggregationExpressionTranslator.Translate(TranslationContext context, MemberInitExpression expression)
   at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.ExpressionToAggregationExpressionTranslator.Translate(TranslationContext context, Expression expression)
   at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.ExpressionToAggregationExpressionTranslator.TranslateLambdaBody(TranslationContext context, LambdaExpression lambdaExpression, IBsonSerializer parameterSerializer, Boolean asRoot)
   at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToPipelineTranslators.SelectMethodToPipelineTranslator.Translate(TranslationContext context, MethodCallExpression expression)
   at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToPipelineTranslators.ExpressionToPipelineTranslator.Translate(TranslationContext context, Expression expression)
   at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToExecutableQueryTranslators.ExpressionToExecutableQueryTranslator.Translate[TDocument,TOutput](MongoQueryProvider`1 provider, Expression expression)
   at MongoDB.Driver.Linq.Linq3Implementation.MongoQuery`2.Execute()
   at MongoDB.Driver.Linq.Linq3Implementation.MongoQuery`2.GetEnumerator()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at Sample.Program5.Main2(String[] args) in D:\tfs2015\Shared\Packages\Panda.DataAccess\Sample\Program5.cs:line 90
   at Sample.Program.Main(String[] args) in D:\tfs2015\Shared\Packages\Panda.DataAccess\Sample\Program.cs:line 21



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

Author:

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

Message: CSHARP-4653: Implement Convert translator for subclasses. (#1101)
Branch: master
https://github.com/mongodb/mongo-csharp-driver/commit/9a8baaa04742e19367327199c6cf0db33a3e56be

Comment by Roberto Pérez [ 15/May/23 ]

Hello again,

 

I am having some problems with line breaks after pasting source code so, please update the "How to reproduce" section with this better displayed and simplified example:

 

    internal class Program5
    {
        public class Preference
        {
            public Guid Id { get; set; }
            public PreferenceType Type { get; set; }
 
            public enum PreferenceType
            {
                EmailAlerts,
                Another
            }
        }
 
        public class EmailAlertsPreference : Preference
        {
            public EmailAlertsPreference()
            {
                Type = PreferenceType.EmailAlerts;
            }
 
            public bool Enabled { get; set; }
            public string Description { get; set; }
        }
 
        internal static void Main2(string[] args)
        {
            foreach (var linqProvider in new []
                     {
                         LinqProvider.V2, 
                         LinqProvider.V3
                     })
            {
                var builder = new MongoUrlBuilder
                {
                    DatabaseName = "Sample",
                    Server = new MongoServerAddress("localhost", 27017)
                };
                var mongoClientSettings = MongoClientSettings.FromUrl(builder.ToMongoUrl());
                mongoClientSettings.LinqProvider = linqProvider;
                var mongoClient = new MongoClient(mongoClientSettings);
                var mongoDatabase = mongoClient.GetDatabase(builder.DatabaseName);
 
                var preferencesCollection = mongoDatabase.GetCollection<Preference>("Preferences");
                preferencesCollection.DeleteMany(c => true);
                preferencesCollection.InsertOne(new EmailAlertsPreference
                {
                    Id = Guid.NewGuid(),
                    Enabled = true,
                    Description = $"{linqProvider} - Description"
                });
 
                var queryableWithoutProjection = preferencesCollection.AsQueryable()
                    .Where(p => p.Type == Preference.PreferenceType.EmailAlerts && ((EmailAlertsPreference)p).Enabled);
 
                Console.WriteLine("*************************");
                Console.WriteLine("Without custom projection");
                Console.WriteLine("*************************");
                foreach (var emailAlertsPreference in queryableWithoutProjection.ToList().Cast<EmailAlertsPreference>())
                {
                    Console.WriteLine(emailAlertsPreference.Enabled);
                    Console.WriteLine(emailAlertsPreference.Description);
                }
                Console.WriteLine("*************************");
                Console.WriteLine();
 
                Console.WriteLine("*************************");
                Console.WriteLine("With custom projection");
                Console.WriteLine("*************************");
                Expression<Func<Preference, EmailAlertsPreference>> projection = p => new EmailAlertsPreference
                {
                    Enabled = ((EmailAlertsPreference)p).Enabled,
                    Description = ((EmailAlertsPreference)p).Description
                };
                foreach (var emailAlertsPreference in queryableWithoutProjection.Select(projection).ToList())
                {
                    Console.WriteLine(emailAlertsPreference.Enabled);
                    Console.WriteLine(emailAlertsPreference.Description);
                }
                Console.WriteLine("*************************");
                Console.WriteLine();
                Console.WriteLine();
            }
        }
    }

Comment by PM Bot [ 15/May/23 ]

Hi suikevil, 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:54 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.