Entity Framework 5 Migrations: Setting up an initial migration and single seed of the database

21,202

Solution 1

This has proved to be a popular post so I have updated it in light of feedback from others. The main thing to know is that the Seed method in the Configuration class is run EVERY time the application starts, which isn't what the comment in the template method implies. See the answer from someone at Microsoft to this post about why that is - thanks to Jason Learmouth for finding that.

If you, like me, only want to run the database updates if there are any pending migrations then you need to do a bit more work. You can find that out if there are pending migrations by calling migrator.GetPendingMigrations(), but you have to do that in the ctor as the list of pending migrations is cleared before Seed method is called. The code to implement this, which goes in the Migrations.Configuration class is as follows:

internal sealed class Configuration : DbMigrationsConfiguration<YourDbContext>
{ 
    private readonly bool _pendingMigrations;

    public Configuration()
    {
        // If you want automatic migrations the uncomment the line below.
        //AutomaticMigrationsEnabled = true;
        var migrator = new DbMigrator(this);
        _pendingMigrations = migrator.GetPendingMigrations().Any();
    }

    protected override void Seed(MyDbContext context)
    {
        //Microsoft comment says "This method will be called after migrating to the latest version."
        //However my testing shows that it is called every time the software starts

        //Exit if there aren't any pending migrations
        if (!_pendingMigrations) return;

        //else run your code to seed the database, e.g.
        context.Foos.AddOrUpdate( new Foo { bar = true});
    }
}

I should point out that some people have suggested putting the seed code in the actual 'up' migration code. This works, but means you need to remember to put the seed code in each new migration and its pretty hard remember so I wouldn't do that. However if your seed changes with each migration then that might be the a good way to go.

Solution 2

You could add a migration manually and fill it with whatever seeding code you want? In package manager console run:

Add-Migration [Name]

You can then edit that file which is created for you in your migrations folder.

In my project i actually do seeding like Richard though in the Seed method of the context configuration. I really have no preference. But migrations should be more efficient in that the application doesn't need to check if the rows exist in the database when the application starts. There is just the need to check if the migration has been run, which should be faster.

internal sealed class Configuration : DbMigrationsConfiguration<MyContext>
{
    public Configuration()
    {
        // If you want automatic migrations as well uncomment below.
        // You can use both manual and automatic at the same time, but I don't recommend it.
        //AutomaticMigrationsEnabled = true;
        //AutomaticMigrationDataLossAllowed = true;
    }

    protected override void Seed(MyContext context)
    {
        //  This method will be called after migrating to the latest version.

        //  You can use the DbSet<T>.AddOrUpdate() helper extension method 
        //  to avoid creating duplicate seed data.

        context.FontFamilies.AddOrUpdate(
            f => f.Id,
            new FontFamily { Id = 1, PcName = "Arial" },
            new FontFamily { Id = 2, PcName = "Times New Roman" },
        });

I'm using this in Global.asax:

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        // Any migrations that haven't been applied before will
        // automatically be applied on Application Pool restart

        Database.SetInitializer<MyContext>(
            new MigrateDatabaseToLatestVersion<MyContext,
            MyApp.Migrations.Configuration>()
        );
    }
}

Solution 3

This is something I've wondered about in the past too. I have certain tables in my database which get populated in my Seed event, and now I just check to see if one of them is empty within the Seed method. If there are rows, the Seed method doesn't run. Not infallible, but does the trick.

Solution 4

The answer to this SO question explains why Seed runs every time the app runs.

I use Jon Smiths method, but I have put the check for pending migrations statement in an #if block like this:

#if (!DEBUG)
            if (!_pendingMigrations) return;
#endif

That way when I'm debugging the Seed method always runs to repopulate my seed data - useful when I do deletes during testing, etc. but I don't get the perf hit when in release.

Share:
21,202
Neil
Author by

Neil

I'm a c++ / c# programmer working for a small company in Dundee, Scotland.

Updated on August 16, 2020

Comments

  • Neil
    Neil over 3 years

    I have an MVC4 app which I've recently upgraded to Entity Framework 5 and I am trying to move our database over to using migrations from the development style of dropping and creating each run.

    Here's what I've done in my app start function.

    protected void Application_Start()
    {
        Database.SetInitializer(
            new MigrateDatabaseToLatestVersion< MyContext, Configuration >() );
        ...
    }
    

    I ran the Enable-Migrations command on my repositories project and I thought that this would create an initial migration file however the only file it created was Configuration

    When I delete the database it creates it as expected via code first and seeds the database from the Configuration file. In the configuration file I changed all the Add() functions to AddOrUpdate()

    However it runs the seed function in my Configuration file each time the website starts and duplicates all the seed data again and again.

    I imagined that it would create an initial migration file as the blog I read suggested that it would and I could put the seed data in there but it didn't

    Can anyone explain how I should be setting up DB in code so that it only seeds once?


    LINK: The migrations blog post I followed


    While this is quite interesting for using the EF migrate.exe I've since switched to using roundhouse for running migrations. I still use EF to scaffold my migrations based on the models but I wrote a little console app to write the migrations out to SQL files. I then use roundhouse to perform the migrations themselves through my rake build scripts. There's a little more process involved but it's much more stable than using EF to perform the migrations on the fly when the application starts up.