Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EF Core 9 generates incorrect compiled models for negative enum sentinel values #35142

Open
Atulin opened this issue Nov 18, 2024 · 4 comments
Open

Comments

@Atulin
Copy link

Atulin commented Nov 18, 2024

EF Core version: 9.0
Database provider: Npgsql (efcore.pg)
Target framework: .NET 9
Operating system: Windows 10
IDE: Rider 2024.3

Configuration:

builder
	.Property(cm => cm.Role)
	.IsRequired()
	.HasDefaultValue(EClubMemberRoles.User)
	.HasSentinel((EClubMemberRoles)(-1));

Compiled model:

var role = runtimeEntityType.AddProperty(
    "Role",
    typeof(EClubMemberRoles),
    propertyInfo: typeof(ClubMember).GetProperty("Role", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
    fieldInfo: typeof(ClubMember).GetField("<Role>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
    valueGenerated: ValueGenerated.OnAdd,
    sentinel: (EClubMemberRoles)-1);
role.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
role.AddAnnotation("Relational:DefaultValue", EClubMemberRoles.User);

As you can see, it omits the parentheses around the -1 which leads to the error.

@cincuranet
Copy link
Contributor

The error is CS0075: To cast a negative value, you must enclose the value in parentheses..

@cincuranet cincuranet changed the title EF Core 9 generates incorrect compiled models for enum sentinel values EF Core 9 generates incorrect compiled models for negative enum sentinel values Nov 19, 2024
@AndriySvyryd AndriySvyryd added this to the 10.0.0 milestone Nov 19, 2024
@ajcvickers ajcvickers assigned ajcvickers and unassigned AndriySvyryd Dec 3, 2024
@ajcvickers
Copy link
Contributor

@Atulin I am not able to reproduce this. Following this change: #31923, the generated code should contain role.SetSentinelFromProviderValue(-1);. This is what I get with my attempt to reproduce this with my code below. Can you please update my code so that it reproduces the error you are seeing, or attach a new, small, runnable project or post a small, runnable code listing that reproduces what you are seeing so that we can investigate.

var role = runtimeEntityType.AddProperty(
    "Role",
    typeof(EClubMemberRoles),
    propertyInfo: typeof(Sensor).GetProperty("Role", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
    fieldInfo: typeof(Sensor).GetField("<Role>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
    valueGenerated: ValueGenerated.OnAdd);
role.SetSentinelFromProviderValue(-1);
role.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
role.AddAnnotation("Relational:DefaultValue", EClubMemberRoles.User);

Repro code:

public class ContextOfDoom : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .EnableSensitiveDataLogging()
            .LogTo(Console.WriteLine, LogLevel.Information)
            .UseNpgsql();

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Sensor>(b =>
        {
            b.Property(cm => cm.Role)
                .IsRequired()
                .HasDefaultValue(EClubMemberRoles.User)
                .HasSentinel((EClubMemberRoles)(-1));
        });
    }
}

public class Sensor
{
    public int Id { get; set; }
    public EClubMemberRoles Role { get; set; }
}

public enum EClubMemberRoles
{
    User,
    Admin
}

@ajcvickers ajcvickers removed this from the 10.0.0 milestone Dec 4, 2024
@Atulin
Copy link
Author

Atulin commented Dec 4, 2024

I don't see the enum registered with NpgSQL according to its documentation. Your code probably treats the value as a plain int because of that, while mine somehow differs because the enums are registered.

I will edit your code shortly, soon as I'm home.

@Atulin
Copy link
Author

Atulin commented Dec 4, 2024

@ajcvickers Here's a reproduction: https://github.com/Atulin/EfEnumTest

ModelEntityType.cs#L51 has the un-parenthesised -1 there. It's also the one enum that has been registered with NpgSQL according to the docs in Program.cs#L16. The unregistered enum uses very different code, however: ModelEntityType.cs#L41) uses .SetSentinelFromProviderValue(-1) instead of the sentinel: argument of .AddProperty() method.

Could it be related to NpgSQL, not to EF itself, after all?

As a side note, when the context is placed in a global namespace, it seems ef migrations add generates invalid code, with an empty using ; (20241204210350_Initial.cs#L1)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants