Efcore: String enum default value results in wrong value on INSERT

Created on 2 Jul 2019  路  8Comments  路  Source: dotnet/efcore

Steps to reproduce:

  1. Create an Entity that has a Enum field
  2. Add 2 or more values to the Enum
  3. Describe enums as strings using OnModelCreating on DbContext
  4. Set a default value different than index 0 of the Enum inside using OnModelCreating on DbContext
  5. Try to insert an Entity that has the Enum field set as index 0 of the Enum
  6. Observe that the inserted value is the default one, instead of the desired index 0 of Enum.

Example:

Visitor.cs
```c#
public class Visitor
{
public int Id { get; set; }
public VisitorState State { get; set; }
}

public enum VisitorState
{
InQueue,
Ordering,
Eating
}

ApplicationDbContext.cs, inside OnModelCreating
```c#
builder.Entity<Visitor>()
    .Property(e => e.State)
    .HasConversion(v => v.ToString(),
        v => (VisitorState)Enum.Parse(typeof(VisitorState), v))
        .HasDefaultValue(VisitorState.Ordering);

Generated Migration
```c#
migrationBuilder.CreateTable(
name: "Visitors",
columns: table => new
{
Id = table.Column(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
State = table.Column(nullable: false, defaultValue: "Ordering")
},
constraints: table =>
{
table.PrimaryKey("PK_Visitors", x => x.Id);
});

Seeding some data anywhere
```c#
this._dbContext.AddRange(new List<Visitor>
{
    new Visitor
    {
        State = VisitorState.InQueue
    },
    new Visitor
    {
        State = VisitorState.Ordering
    },
    new Visitor
    {
        State = VisitorState.Eating
    }
});

Database Result

| Id | State |
|---:|----------|
| 1 | Ordering |
| 2 | Ordering |
| 3 | Eating |

Expected Result

| Id | State |
|---:|----------|
| 1 | InQueue |
| 2 | Ordering |
| 3 | Eating |

Solution that reproduces the issue

EnumIssue.zip

Thanks in advance.

closed-question

Most helpful comment

@vdurante Because if you want EF to sometimes insert a real value, and sometimes use the database default, then EF has to be able to determine when to do one verses the other. It does this based on whether the value in the given entity is the CLR default or not. However, if the CLR default is an actual value that can be inserted, then it can't act as the sentinel. In this case, the property should be made nullable so that null becomes the CLR default and the database default will therefore not be used unless the value is null.

All 8 comments

@vdurante What is your expectation of what HasDefaultValue should do in this case?

@ajcvickers If I attempt to insert a NULL State, it should insert the default value, which is Ordering in this case. In my example I attempt to insert a value of InQueue, but in the database it inserts the wrong value of Ordering.

I have edited my issue to include the generated migration.

@vdurante Typically you need a nullable property for that, otherwise there is no null State.

@ajcvickers either way, what explains the fact I am inserting the value InQueue but it gets inserted as Ordering in the database? I am not trying to insert NULL. I am explicitly inserting a value and the wrong value is being inserted.

@vdurante Because if you want EF to sometimes insert a real value, and sometimes use the database default, then EF has to be able to determine when to do one verses the other. It does this based on whether the value in the given entity is the CLR default or not. However, if the CLR default is an actual value that can be inserted, then it can't act as the sentinel. In this case, the property should be made nullable so that null becomes the CLR default and the database default will therefore not be used unless the value is null.

@ajcvickers I guess I understood what is happening.

So, EF considers the CLR default is 0 since the attribute cannot be NULL? From my understanding of your comment, since it is a non-nullable enum, EF considers the default 0, so it maps position 0 of the enum (InQueue) to the default.

@vdurante Yes. Zero is the CLR default for any enum.

@ajcvickers thank you!

Was this page helpful?
0 / 5 - 0 ratings