10

I'm trying to automatically generate my database if it doesn't exists and run the Seed() method to populate the data. In my Database Context constructor I have this:

Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDBContext, Configuration>());

This works great, my database is automatically created with all the tables as I want, but it seems like the Seed() method is not being called, my database is empty. This is my class:

internal sealed class Configuration : DbMigrationsConfiguration<Context.MyDBContext>
{

    public Configuration()
    {
        AutomaticMigrationsEnabled = true;
    }

    protected override void Seed(Context.MyDBContext context)
    {
        context.Users.AddOrUpdate(
            new Entities.User() { Email = "default@default.com", Password = "", Language = "en", CreatedDate = DateTime.Now }
        );

        base.Seed(context);
    }
}

When I run Update-Database in the Nuget console the data is populated after database creation, but with MigrateDatabaseToLatestVersion the Seed() method is not called.

What can be happening? I tried manually running migrations as taken from here:

var configuration = new MyDbContextConfiguration();
configuration.TargetDatabase = new DbConnectionInfo(
    database.ConnectionString, database.ProviderName);

var migrator = new DbMigrator(configuration);
migrator.Update();

But also doesn't work.


EDIT:

Ok, after some more testing I found that the Seed() method runs but only when the database already exists, that is, on the first run when the database is created for the first time the Seed() method is not executed, but when I run my app the second time Seed() get's executed. I also had to add context.SaveChanges() in order for it to work (thanks to @DavidG in the comments):

protected override void Seed(Context.MyDBContext context)
    {
        context.Users.AddOrUpdate(
            new Entities.User() { Email = "default@default.com", Password = "", Language = "en", CreatedDate = DateTime.Now }
        );

        context.SaveChanges();
        base.Seed(context);
    }

So maybe I can manually call Seed() inside Configuration() and do some checking to avoid adding duplicating data or modifying data that already exists.

Community
  • 1
  • 1
Andres
  • 6,080
  • 13
  • 60
  • 110
  • Doesn't work is not enough. Did you try to step into with a debugger? Is the Seed method being called? If it is, the problem might be trying to initialize an user that way. Use UserManager instead – Royal Bg Mar 16 '16 at 00:02
  • Can you throw an exception in the `Seed` method to verify that the seed method was not run at all (and not that simply the user wasn't inserted into the database?). – Rob Mar 16 '16 at 00:03
  • You are not calling `context.SaveChanges();` after you add your users. – DavidG Mar 16 '16 at 00:06
  • @DavidG save changes is not necessary in `Seed()`. Probably the base.Seed() is calling it. – Royal Bg Mar 16 '16 at 00:07
  • @RoyalBg Yes, you may be right there. I always explicitly call `SaveChanges` so I can't remember the default behaviour! Perhaps we an see the context class too? – DavidG Mar 16 '16 at 00:14
  • @Royal Bg Yes, I used the debugger and it's not being called. Will update the question with the context class. – Andres Mar 16 '16 at 00:27
  • Did you try also to manually rund from powershell console `Update-Database`? Once I hit a rare scenario I need to register the initializer in the app.config too – Royal Bg Mar 16 '16 at 00:34
  • @RoyalBg With the nugget console, running Update-Database creates the database and the data is populated as it should. – Andres Mar 16 '16 at 00:47
  • This is the expected behavior btw. If you want anytime you start the application to create db and seed, you have to ensure you manually call the `Seed` method (e.g. in the config constructor). Only `Update-Database` ensures calling the seed automatically. If you have a build script (recipe), add `Update-Database` to the batch. – Royal Bg Mar 16 '16 at 00:50
  • @RoyalBg No, I want the database to be created when it doesn't exists and after new migrations are executed the Seed() method should be executed too – Andres Mar 16 '16 at 00:56
  • I'm out of ideas, but you can still try to add the initializer to the xml - http://www.asp.net/media/4367421/mig.png – Royal Bg Mar 16 '16 at 01:08
  • You know there is another Seed() method on your initializer that only runs when the database is created as opposed to migration seed that runs with every update-database. I use the initializer seed during early development then switch to migrations when app is out in other enviroments. http://blog.oneunicorn.com/2013/05/28/database-initializer-and-migrations-seed-methods/ – Steve Greene Mar 16 '16 at 13:20
  • @SteveGreene in this case that's incorrect, as he is using the MigrateDatabaseToLatestVersion initializer which has no Seed() method available for override. – NJH Jan 02 '17 at 23:18

1 Answers1

8

I ended up with this Configuration class:

public class Configuration : DbMigrationsConfiguration<Context.MyDBContext>
    {
        private readonly bool _pendingMigrations;

        public Configuration()
        {
            AutomaticMigrationsEnabled = true;

            // Check if there are migrations pending to run, this can happen if database doesn't exists or if there was any
            //  change in the schema
            var migrator = new DbMigrator(this);
            _pendingMigrations = migrator.GetPendingMigrations().Any();

            // If there are pending migrations run migrator.Update() to create/update the database then run the Seed() method to populate
            //  the data if necessary
            if (_pendingMigrations)
            {
                migrator.Update();
                Seed(new Context.MyDBContext());
            }
        }

        protected override void Seed(Context.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

            // Run whatever you like here

            // Apply changes to database
            context.SaveChanges();
            base.Seed(context);
        }
    }

So on this way the Seed() method is called when the database is created and also when there are pending migrations.

This is my MyDBContext class:

public class MyDBContext: DbContext
{
    public MyDBContext() : base(AppSettings.DBConnectionString)
    {
    }

    public static MyDBContext Create()
    {
        return new MyDBContext();
    }

    public DbSet<User> Users { get; set; }
    public DbSet<Entries> Entries { get; set; }
}
Andres
  • 6,080
  • 13
  • 60
  • 110
  • 4
    I have same problem, but your solution creates infinite loop for me. To be specific this part `Seed(new Context.MyDBContext());` It create new `DbContext` which creates new `Configuratio` which creates new `DbContext` and so on until StackOverflow exception – Masius Apr 17 '17 at 20:04
  • See the example `MyDBContext` class I added to the answer. For me it's working perfectly in a production environment with no problems at all. – Andres Apr 17 '17 at 20:10
  • Where `Database.SetInitializer(new MigrateDatabaseToLatestVersion());` should now be called - it is missing in `MyDbContext` constructor? – pitersmx Jun 14 '17 at 11:55
  • `Database.SetInitializer(new MigrateDatabaseToLatestVersion());` is called on your application startup so it will check the if the database exists and apply any pending migrations – Andres Jun 14 '17 at 16:51
  • Okay, I found the problem. Very nice solution by the way. – pitersmx Jun 14 '17 at 20:31
  • 1
    `_pendingMigrations = migrator.GetPendingMigrations().Any();` crashes Visual Studio 2017 when calling `Add-Migration`. Verified on two different dev machines. Why? – starmandeluxe Aug 21 '17 at 06:41