dotnet / dotnet/efcore

Default Value + HasData generates incorrect migration

Open
#36,283 8 comments 1 reaction 1 assignee Claimed by @AndriySvyryd View on GitHub
area-migrations-seeding customer-reported
Dominant language
C#
Stars
14.8k
Forks
3.4k
PR merge metrics
PR metrics pending

Description

### Bug description

Hey folks,

I'm in the middle of a project right now, and I'm using `HasData` to seed values for static, lookup type tables. I just discovered that there's a bizarre behavior in the migration generation when default values are specified on a non-nullable boolean column.

Inside of `HasData`, I'm supplying a new entity with the boolean set to `false`. The result is that the migration excludes that value from the migration, resulting in a `true` value being inserted into the database. My expectation is that it would insert the values from the entity explicitly.

I've tested this behavior on both the postgres and sqlite providers with the same result. Strangely enough, the model snapshot has the value. I'm using VS Code and the EF global tool

I have a workaround for the behavior, but it's far from ideal:

1. Remove the default value
2. Generate a migration
3. Copy the generated (correct) data insertion statements
4. Remove the migration
5. Add the default value back
6. Paste the correct statements in place of the generated (incorrect) statements

Test code provided below

### Your code

```csharp
// Program.cs

builder.Services.AddDbContext(options =>
options.UseSqlite());

// Test Model
public class TestModel
{
public int Id { get; set; }
public bool IsWorking { get; set; }
}

// Context.cs
public partial class TestContext : DbContext
{
public virtual DbSet TestModels { get; set; }
public TestContext(DbContextOptions options)
: base(options)
{
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity(entity =>
{
entity.HasKey(e => e.Id);

entity.ToTable("test_model");

entity.Property(e => e.Id).HasColumnName("id");

entity.Property(e => e.IsWorking)
.HasColumnName("is_working")
.HasDefaultValue(true);

entity.HasData(new TestModel()
{
Id = 1,
IsWorking = false,
});
});
}
}

// Migration
public partial class test_sqlite_default_true : Migration
{
///
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "test_model",
columns: table => new
{
id = table.Column(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
is_working = table.Column(type: "INTEGER", nullable: false, defaultValue: true)
},
constraints: table =>
{
table.PrimaryKey("PK_test_model", x => x.id);
});

migrationBuilder.InsertData(
table: "test_model",
column: "id",
value: 1);
}

///
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "test_model");
}
}

// Model snapshot
partial class TestContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "9.0.6");

modelBuilder.Entity("TestModel", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("id");

b.Property("IsWorking")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasDefaultValue(true)
.HasColumnName("is_working");

b.HasKey("Id");

b.ToTable("test_model", (string)null);

b.HasData(
new
{
Id = 1,
IsWorking = false
});
});
#pragma warning restore 612, 618
}
}
```

### Stack traces

```text

```

### Verbose output

```text

```

### EF Core version

9.0.3

### Database provider

Microsoft.EntityFrameworkCore.Sqlite; Npgsql.EntityFrameworkCore.PostgreSQL

### Target framework

.NET 9.0; .NET 8.0

### Operating system

Mac

### IDE

VS Code

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.