ErikEJ / EFCorePowerTools

Entity Framework Core Power Tools - reverse engineering, migrations and model visualization in Visual Studio & CLI
MIT License
2.13k stars 294 forks source link

Default value exception #2238

Closed zackhowe closed 6 months ago

zackhowe commented 6 months ago

When using the example t4 template from your repo(https://github.com/ErikEJ/EFCorePowerTools/blob/master/samples/CodeTemplates/EFCore/EntityType.t4), and using an enum type for a field, when generating DbContext, the default value will cause an error.

for example, the OnModelCreating entry for the class will produce something like: modelBuilder.Entity(entity => { entity.property(e => e.EnumField).HasDefaultValue((byte)0); };

which will generate an exception when the DbContext is first referenced and the Model is built, as setting your EnumField to Byte 0 is not allowed.

When using the 703 Template, the model was build with

.HasDefaultSqlValue((0)) and worked.

Thoughts: The DbContext.t4 doesn't know about the enums, so handling it in the OnModelCreating seems challenging (Also do you need to have an option to provide the name of the Default Enum?)

But if DataAnnotations are selected in EFPT, a [DefaultValue(EnumType.DefaultEnumValue)] could be added in the Entities...or even a [DefaultValue((EnumType)0)] perhaps?

Provide technical details

ErikEJ commented 6 months ago

Remove the default value?

zackhowe commented 6 months ago

A simple and elegant solution, except I don't "own" the database in this case, so changing the table DDL is not really any option for me. I'll pursue that with the DB team, but would love to find a way to make the EFPT work even if they want to keep the default value set.... I think the Data Annotation method would be a nice way, but it still leaves an issue if the OnModelCreating method is used for defaults..and am I correct in my guess that the DbContext.t4 knows nothing about the enumList, let alone anything about which value might be "default"? ie, if my database default was 1 because my enum was enum : byte { Error = 0, Default = 1, AnotherValue = 2 ....}

ErikEJ commented 6 months ago

for example, the OnModelCreating entry for the class will produce something like: modelBuilder.Entity(entity => { entity.property(e => e.EnumField).HasDefaultValue((byte)0); };

which will generate an exception when the DbContext is first referenced and the Model is built, as setting your EnumField to Byte 0 is not allowed.

When using the 703 Template, the model was build with

.HasDefaultSqlValue((0)) and worked.

@ajcvickers Thoughts?

ErikEJ commented 6 months ago

@zackhowe Wonder if a feature to remove default values from tinyint columns would work? Like I have today for bit columns.

zackhowe commented 6 months ago

It might. I doubt the problem is limited to tinyint though? I haven't tried it with other types of enums (based on short/int) but I suspect it would have the same result.
The default would still be applied by the database, assuming no other value was set, but it would be kinda neat to have it on the POCO entity while processing it, if the value was something other than the natural default (ie 0) The main issue seems to be that the .HasDefaultValue uses an Object parameter, and then internally casts it to the target type of the class, and a cast from byte (or any other type?) to the enum is not allowed. I am guessing because it is using some sort of reflection or implicit cast rather than explicit cast, which should be valid? But since the template only knows about the database side, it is using the byte cast since it has the database type/mapping available to it, but not the POCO mapping (or the knowledge of which enum value the database default would map to)

ErikEJ commented 6 months ago

I think your best option at this point is to add additional properties in a partial class and map to the underlying byte value.

ajcvickers commented 5 months ago

See https://github.com/dotnet/efcore/issues/32539