Jakub Arnold
Jakub Arnold

Reputation: 87220

What are the rules/requirements for code-first migrations and what do I need to do to make them happen?

I'm getting a little confused by the different approaches to migrations in Entity Framework 6.1.1

Up until now it was my understanding that I have to always run Update-Database in order to update the schema so that it reflects my newly added models. But now I just managed to create a POCO model, add a DbSet<Foo> Foos {get;set;} to my ApplicationDbContext, re-build the application and everything magically works.

Previously I remember having to run Enable-Migrations and then turn on the automatic migrations and always call Update-Database to migrate, but it seems this is not necessary after all?

Just to clarify, I'm using the ASP.NET Web Application template with MVC and Internet authentication (just the basic user accounts that are generated by the scaffold).

There seem to be just so many different approaches to migrations and so many tutorials, where every one of them does it a bit differently, that I'm not sure which approach is actually the correct one. It feels to me that I'm mixing together things that could be kept separate. Most of the resources I found online are also referencing different versions of EF and ASP.NET MVC.

What are the steps needed to be able to generate my database schema based on my POCO models? Is there an easy way to just re-create and re-seed the database as needed automatically, or do I have to keep using Update-Database after that?

I'm using Visual Studio 2013 Ultimate if that's of any relevance.

Upvotes: 1

Views: 1144

Answers (2)

Yuliam Chandra
Yuliam Chandra

Reputation: 14640

  • Up until now it was my understanding that I have to always run Update-Database in order to update the schema so that it reflects my newly added models.

    You can run the migration from Package Manager Console, initializer or code.

    • From PMC PM> Update-Database
    • From initializer Database.SetInitializer(new MigrateDatabaseToLatestVersion<AppContext, Configuration>()); or DbMigrator(new Configuration()).Update()
    • From code new DbMigrator(new Configuration()).Update();
  • But now I just managed to create a POCO model, add a DbSet Foos {get;set;} to my ApplicationDbContext, re-build the application and everything magically works.

    If you have exiting database and there is a new changes in your model, e.g. by adding a new DbSet<T> or just adding a new property, without setting any initializer / migration (in the code or in the config), you should get error like.

    The model backing the 'AppContext' context has changed since the database was created. Consider using Code First Migrations to update the database.

    You probably have set somewhere to use MigrateDatabaseToLatestVersion and set AutomaticMigrationsEnabled = true;.

  • and then turn on the automatic migrations and always call Update-Database to migrate, but it seems this is not necessary after all?

    Yes, it's not necessary anymore once you have enabled automatic migration.

  • What are the steps needed to be able to generate my database schema based on my POCO models?

    If you want to maintain the database version manually:

    • Create configuration class that is derived from DbMigrationsConfiguration<TContext> or just use PMC PM> Enable-Migrations
    • Create DbMigration manually by writing the sql query for each version or just use PMC PM> Add-Migration MigrationNameForVersioning
    • Disable the automatic migration AutomaticMigrationsEnabled = false
    • Set the initializer to MigrateDatabaseToLatestVersion

    If you just want to automatically update the database without creating each migration version manually.

    • Create configuration class that is derived from DbMigrationsConfiguration<TContext> or just use PMC PM> Enable-Migrations
    • Enable the automatic migration AutomaticMigrationsEnabled = true
    • Set the initializer to MigrateDatabaseToLatestVersion

Code

internal sealed class Configuration : DbMigrationsConfiguration<AppContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = false;
        ContextKey = "AppContext"; // don't forget to match this key if create the class manually
    }

    protected override void Seed(AppContext context)
    {
    }
}

Usage

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

More

Upvotes: 1

drneel
drneel

Reputation: 2907

I believe you have multiple questions here, so I'll address them as best I can.

First:

I just managed to create a POCO model, add a DbSet Foos {get;set;} to my ApplicationDbContext, re-build the application and everything magically works.

It seems as if you have Automatic Code First Migrations turned on for your project. From the link provided, it seems that the automatic migrations are not enabled by default, so something must have changed on your end either by running the command, or by using custom code (shown later)

From MSDN

If you were to run the application again you would get an InvalidOperationException stating The model backing the 'BlogContext' context has changed since the database was created. Consider using Code First Migrations to update the database ( http://go.microsoft.com/fwlink/?LinkId=238269).

As the exception suggests, it’s time to start using Code First Migrations. Because we want to use automatic migrations we’re going to specify the –EnableAutomaticMigrations switch.

Run the Enable-Migrations –EnableAutomaticMigrations command in Package Manager Console

Next:

What are the steps needed to be able to generate my database schema based on my POCO models?

Well, you have several options here; EF Automatic Migrations, EF 'Standard' Migrations, third party library migrations (FluentMigrator). This would be to broad of a question by itself. But any and all options are valid, it just depends on your needs. I'm currently using EF Automatic Migrations for a personal project and FluentMigrator at work.

Finally:

Is there an easy way to just re-create and re-seed the database as needed automatically, or do I have to keep using Update-Database after that?

What are you trying to do here is the important question. I'm pretty sure you wouldn't want to drop and recreate / reseed the database in production (although that is possible).

You'll probably want to use some sort of DBInitializer to fit your needs. Here is a tutorial I used when researching this a few months back. You have 3 default options (CreateDatabaseIfNotExists, DropCreateDatabaseIfModelChanges, DropCreateDatabaseAlways), and of course the ability to customize your own.

Another option available is to write a custom DbMigrationsConfiguration . I can't find the article where I read about that, but here is some sample code. In this code, I have my DBContext initializing using my custom configuration class in which I have turned on EF Automatic Migrations and populate my database with some default data.

DBContext

public ApplicationDBContext()
    : base("DefaultConnection")
{
    Database.SetInitializer(new MigrateDatabaseToLatestVersion<ApplicationDBContext, IBCF.Core.Migrations.Configuration>());
}

Configuration

internal sealed class Configuration : DbMigrationsConfiguration<IBCF.Core.ApplicationDBContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = true;
    }

protected override void Seed(IBCF.Core.ApplicationDBContext context)
{
    //Initialize managers
    var userManager = new UserManager<User>(new UserStore<User>(context));
    var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(context));

    if(userManager.FindByEmail("[email protected]") == null)
    {
        //Add default system roles
        roleManager.Create(new IdentityRole("Superuser"));
        roleManager.Create(new IdentityRole("User"));

        //Add default system users
        var superuser1 = new User() { FirstName = "John", LastName = "Doe", Email = "[email protected]", UserName = "[email protected]" };
        if (userManager.Create(superuser1, "P@ssword123").Succeeded)
        {
            userManager.AddToRole(superuser1.Id, "Superuser");
        }
    }

    context.SaveChanges();
}
}

Upvotes: 1

Related Questions