Migrating to Entity Framework Core Seed Data

Microsoft .NET

I was already using Entity Framework Core 2.0 and had written some custom code to enter some static seed data to certain tables. Entity Framework 2.1 added support for data seeding which manages your seed data for you and adds them to your Entity Framework Core migrations.

The problem is that if you've already got data in your tables, when you add a migration containing seed data, you will get exceptions thrown as Entity Framework tries to insert data that is already there. Entity Framework is naive, it assumes that it is the only thing editing the database.

Migrating to using data seeding requires a few extra steps that aren't documented anywhere and weren't obvious to me. Lets walk through an example. Assuming we have the following model and database context:

public class Car
{
    public int CarId { get; set; }

    public string Make { get; set; }

    public string Model { get; set; }
}

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions options)
        : base(options)
    {
    }

    public DbSet<Car> Cars { get; set; }
}

We can add some seed data by overriding the OnModelCreating method on our database context class. You need to make sure your seed data matches the existing data in your database.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Car>().HasData(
        new Car() { CarId = 1, Make = "Ferrari", Model = "F40" },
        new Car() { CarId = 2, Make = "Ferrari", Model = "F50" },
        new Car() { CarId = 3, Make = "Lambourghini", Model = "Countach" });
}

If we run a command to add a database migration, the generated code looks like this:

dotnet ef migrations add AddSeedData
public partial class AddSeedData : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.InsertData(
            table: "Cars",
            columns: new[] { "CarId", "Make", "Model" },
            values: new object[] { 1, "Ferrari", "F40" });

        migrationBuilder.InsertData(
            table: "Cars",
            columns: new[] { "CarId", "Make", "Model" },
            values: new object[] { 2, "Ferrari", "F50" });

        migrationBuilder.InsertData(
            table: "Cars",
            columns: new[] { "CarId", "Make", "Model" },
            values: new object[] { 3, "Lambourghini", "Countach" });
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DeleteData(
            table: "Cars",
            keyColumn: "CarId",
            keyValue: 1);

        migrationBuilder.DeleteData(
            table: "Cars",
            keyColumn: "CarId",
            keyValue: 2);

        migrationBuilder.DeleteData(
            table: "Cars",
            keyColumn: "CarId",
            keyValue: 3);
    }
}

This is what you need to do:

  1. Comment out all of the InsertData lines in the generated migration.
  2. Run the migration on your database containing the existing seed data. This is effectively doing a null operation but records the fact that the AddSeedData migration has been run.
  3. Uncomment the InsertData lines in the generated migration so that if you run the migrations on a fresh database, seed data still gets added. For your existing databases, since the migration has already been run on them, they will not add the seed data twice.

That's it, hope that helps someone.

Web Mentions

What's this?

0 Replies

GitHub Comments

RehanSaeed

Martín Coll Martín Coll commented on 2018-07-02 01:01:09

Yep, seed data works the same as the rest of your model now. The process you describe is exactly the same you'd do if you had manually changed the schema AND THEN decided to write the model classes and generating the migration.

Thanks for taking the time to understand it and blog about it!

RehanSaeed

Muhammad Rehan Saeed Muhammad Rehan Saeed commented on 2018-07-02 09:01:54

Yep, seed data works the same as the rest of your model now. The process you describe is exactly the same you'd do if you had manually changed the schema AND THEN decided to write the model classes and generating the migration.

Thanks for taking the time to understand it and blog about it!

Yes, it took me more time than I care to admit to get my head around how it all works. The key is to think of EF Core migrations as a dumb script runner that blindly runs through the code in your migrations and throws exceptions if it finds anything out of the ordinary.

RehanSaeed

Alexander Ekzarov Alexander Ekzarov commented on 2018-08-14 18:25:40

No working for 2.1. HasData always generate Delete/Insert stuff in migration Up/Down methods even if no data was changed while adding new migrations.

RehanSaeed

Muhammad Rehan Saeed Muhammad Rehan Saeed commented on 2018-08-16 14:51:32

No working for 2.1. HasData always generate Delete/Insert stuff in migration Up/Down methods even if no data was changed while adding new migrations.

Raise an issue on GitHub or StackOverflow. It works for me.

RehanSaeed

Alireza Haghshenas Alireza Haghshenas commented on 2018-09-21 01:25:16

No working for 2.1. HasData always generate Delete/Insert stuff in migration Up/Down methods even if no data was changed while adding new migrations.

Same problem here. I add a few entities, one has data, and ef decides to delete/re-insert seed data from some of the existing entities. I could not find a logic behind why it does that and which entities it decides to delete. I check the source control history of the DbContext file as well as model snapshot and I'm pretty sure the only changes is adding new entities with data and not changing on existing model/data.

The problem I have is that I tried to reproduce it using a smaller mode, but failed to do that.

RehanSaeed

ali ali commented on 2018-10-01 11:32:53

There is already an object named in EF Core 2 next change model ?

RehanSaeed

Muhammad Rehan Saeed Muhammad Rehan Saeed commented on 2018-10-23 09:49:18

There is already an object named in EF Core 2 next change model ?

Not sure I understand you.

RehanSaeed

Alok Alok commented on 2018-11-12 14:47:40

Hi I am using Microsoft Entity Framework Core 2.0.1 and using OnModelCreating method to insert initial data into the database while database is created, but having compile time error while using HasData method, showing "could not be found or showing are you missing a directive".
Please shade some light of it.

RehanSaeed

Muhammad Rehan Saeed Muhammad Rehan Saeed commented on 2018-11-12 16:02:14

Hi I am using Microsoft Entity Framework Core 2.0.1 and using OnModelCreating method to insert initial data into the database while database is created, but having compile time error while using HasData method, showing "could not be found or showing are you missing a directive".
Please shade some light of it.

Move to 2.1 and use data seeding. It will be less work.

bistok

Alireza Haghshenas Alireza Haghshenas commented on 2018-09-21 01:25:16

No working for 2.1. HasData always generate Delete/Insert stuff in migration Up/Down methods even if no data was changed while adding new migrations.

Same problem here. I add a few entities, one has data, and ef decides to delete/re-insert seed data from some of the existing entities. I could not find a logic behind why it does that and which entities it decides to delete. I check the source control history of the DbContext file as well as model snapshot and I'm pretty sure the only changes is adding new entities with data and not changing on existing model/data.

The problem I have is that I tried to reproduce it using a smaller mode, but failed to do that.

I have this problem with some types and what I found is that the Id was Auto Generated like Guid.NewGuid, the way I fixed it is that always specify a value for the key properties and that fixed the problem and now every migration do not delete and creates the objects.