SmartEnum icon indicating copy to clipboard operation
SmartEnum copied to clipboard

ConfigureSmartEnum not working

Open ghost opened this issue 1 year ago • 7 comments

this is my ConfigureConventions image

The smart enum image

How I use it under entity image

The exception 'No suitable constructor was found for entity type 'UnitMaterial'. The following constructors had parameters that could not be bound to properties of the entity type: Cannot bind 'name', 'value' in 'UnitMaterial(string name, int value)'

how can I fix this ? please help UnitMaterial and Entity is under my DOMAIN project, and dbContext is under INFRASTRUCTURE project

My specs .NET 8.0 SmartEnum 8.1 SmartEnum.EFCore 8.1

ghost avatar Nov 07 '24 09:11 ghost

can someone please help on this

HAOYI99 avatar Nov 21 '24 09:11 HAOYI99

It looks right as is - if you create a minimal repo I can take a look.

As a workaround you can try this:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<YourEntity>()
        .Property(e => e.Unit)
        .HasConversion(
            v => v.Value, // Convert `SmartEnum` to its `value` (int)
            v => UnitMaterial.FromValue(v) // Convert `value` (int) back to `SmartEnum`
        );
}

ardalis avatar Nov 21 '24 20:11 ardalis

It looks right as is - if you create a minimal repo I can take a look.

As a workaround you can try this:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<YourEntity>()
        .Property(e => e.Unit)
        .HasConversion(
            v => v.Value, // Convert `SmartEnum` to its `value` (int)
            v => UnitMaterial.FromValue(v) // Convert `value` (int) back to `SmartEnum`
        );
}

Hi @ardalis, thanks for the reply.

yes, I did use this method to manual map the conversion for each SmartEnum.

However, the document mentioned ConfigureSmartEnum is sufficient for configuring all SmartEnum.

I tried many times, ends up I create a SmartEnumConverter myself.

public class SmartEnumConverter<TEnum>() : ValueConverter<TEnum, string>(
    enumValue => enumValue.Name,
    stringValue => SmartEnum<TEnum, int>.FromName(stringValue, true)
) where TEnum : SmartEnum<TEnum, int>;

and the builder.ConfigureSmartEnum(); is not required with this approach

public static void ConfigureSmartEnum<TEnum>(this ModelConfigurationBuilder builder) where TEnum : SmartEnum<TEnum, int>
{
     builder.Properties<TEnum>().HaveConversion<SmartEnumConverter<TEnum>>();
}
protected override void ConfigureConventions(ModelConfigurationBuilder builder)
{
    base.ConfigureConventions(builder);
    builder.Properties<DateOnly>().HaveConversion<DateOnlyConverter>().HaveColumnType("date");
    builder.Properties<DateTime>().HaveColumnType("datetime");
    builder.Properties<TimeSpan>().HaveConversion<long>();
    builder.Properties<Enum>().HaveConversion<string>();
    builder.ConfigureSmartEnum<UnitMaterial>();
    builder.ConfigureSmartEnum<AmTechnologies>();
}

HAOYI99 avatar Nov 22 '24 01:11 HAOYI99

We are running into this same error when generating migrations since moving to .NET 9. Any entity that has changed since the previous migration and contains a smart enum generates this exception during the migration generation process, unless the workaround given by @ardalis is used in the entity configuration.

I should note that this doesn't seem to effect runtime operation at all, it only seems to be required during the migration generation process if that helps troubleshooting at all.

bengavin avatar Nov 22 '24 13:11 bengavin

Yeah EF Core must have changed something in v9 I guess. We'll take a look and figure out how to do a more general solution (again) as soon as we can. Community assistance appreciated as always, including reproductions of the problem in clean repos so if we come up with a fix we can test it with your scenario.

ardalis avatar Nov 22 '24 21:11 ardalis

I encountered a similar issue:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.ConfigureSmartEnum(); // Won't work here
        modelBuilder.ApplyConfigurationsFromAssembly(typeof(DbCtx).Assembly);
    }

I had to add it after ApplyConfigurationsFromAssembly, which seemed to do the trick.

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.ApplyConfigurationsFromAssembly(typeof(DbCtx).Assembly).ConfigureSmartEnum(); // Works here
    }

jczacharia avatar Mar 14 '25 01:03 jczacharia

Experimentally, it seems that calling HaveConversion after configurationBuilder.CreateModelBuilder now has no effect. I worked around this issue by getting the list of all enum property types in the assembly from Assembly.GetExecutingAssembly().GetTypes() (and then the same derived type filters) rather than using modelBuilder.Model.GetEntityTypes(), to avoid the CreateModelBuilder call. Not sure if that's a good general solution, but it at least avoids needing to register each enum individually.

gracewhitney avatar Oct 24 '25 15:10 gracewhitney