ardalis / SmartEnum

A base class for quickly and easily creating strongly typed enum replacements in C#.
MIT License
2.15k stars 169 forks source link

EF Core 7 InMemory - Throwing System.ArgumentException with any query involving a SmartEnum #400

Open jbaconCCG opened 1 year ago

jbaconCCG commented 1 year ago

Upgrading my solution to .NET 7 and suddenly hitting the following exception when running unit tests:

System.ArgumentException : Expression of type 'Ardalis.SmartEnum.SmartEnum'2[Namespace.ReserveCostGroupEnum,System.String]' cannot be used for parameter of type 'System.String' of method 'Namespace.ReserveCostGroupEnum GetFromValue(System.String)' (Parameter 'arg0')

Unsure if the problem lies with the Microsoft.EntityFrameworkCore.InMemory package or SmartEnum. I am not getting the issue running through SQL server, only through unit tests using an InMemory db. Tried applying the Conversion through:

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
        {
            configurationBuilder.ConfigureSmartEnum();
        }

and

builder.Property(p => p.ReserveCostGroup).HasColumnType("varchar(20)").HasConversion(p => p.Value, p => ReserveCostGroupEnum.FromValue(p));

I've upgraded all the SmartEnum packages to the newest available. An example of a simple query that fails:

var enumTest = await dbContext.Test_Table.Where(q => q.ReserveCostGroup == ReserveCostGroupEnum.SubContractor).ToListAsync()

an example of the SmartEnum:

public sealed class ReserveCostGroupEnum : SmartEnum<ReserveCostGroupEnum, string>
    {
        public static readonly ReserveCostGroupEnum None = new ReserveCostGroupEnum(nameof(None), "None");
        public static readonly ReserveCostGroupEnum Buildings = new ReserveCostGroupEnum(nameof(Buildings), "Buildings");
        public static readonly ReserveCostGroupEnum Contents = new ReserveCostGroupEnum(nameof(Contents), "Contents");
        public static readonly ReserveCostGroupEnum Expenses = new ReserveCostGroupEnum(nameof(Expenses), "Expenses");
        public static readonly ReserveCostGroupEnum SubContractor = new ReserveCostGroupEnum(nameof(SubContractor), "SubContractor");
        public static readonly ReserveCostGroupEnum Costs = new ReserveCostGroupEnum(nameof(Costs), "Costs");
        public ReserveCostGroupEnum(string name, string value) : base(name, value)
        {
        }
    }

Any help would be greatly appreciated!

ardalis commented 1 year ago

Can you share what version(s) of EF Core and SmartEnum libraries you're on? I know you said latest but just confirming so I can reproduce with the same stuff you have. Thanks!

jbaconCCG commented 1 year ago

Yeah no worries:

Ardalis.SmartEnum 7.0.0 Ardalis.SmartEnum.EFCore 7.0.0 Microsoft.EntityFrameworkCore 7.0.5 Microsoft.EntityFrameworkCore.InMemory 7.0.5

Thanks for taking a look!

jbaconCCG commented 1 year ago

Dug into it a bit more today and the issue seems to only happen when checking for null using operators, it works fine when using '.Equals'. i.e.:

            ClaimOutcomeEnum outcome = await dbContext.Table_Test
                    .Where(q => !q.ClaimOutcome.Equals(null))
                    .Select(t => t.ClaimOutcome)
                    .FirstOrDefaultAsync();

This works fine, however

            ClaimOutcomeEnum outcome = await dbContext.Table_Test
                    .Where(q => q.ClaimOutcome != null)
                    .Select(t => t.ClaimOutcome)
                    .FirstOrDefaultAsync();

bombs out with 'System.ArgumentException'. (sorry, I know this is different to my original example!)