CodeDigest.Com Logo
Featured:

What is Entity Framework Code First Migration? A Beginners Guide

Entity Framework Code First helps us to create domain model first using POCO classes and delegate the database creation process to Entity Framework. This is called Domain Driver Design, where the developers can design the domain model before designing the database model.

Read the Quick Start Guide Learn Entity Framework Code First in 10 Minutes to have quick understanding on EF Code First approach. Until, EF 4.3, the only way to manage the database creation from Code First model is using the Database Initializers. As mentioned in the quick start guide, the database initializers are very basic and does not support incremental updations to database schema which is how we will manage database changes in real time. For dealing with this, EF 4.3 released a special tool called Entity Framework Code First Migrations which allows us to manage the database changes that are changing continuously.

Let’s see how to use code first migrations and update the database schema incrementally.

There are primarily 2 ways of performing Code First Migrations,

  1. Code Based Migrations

  2. Automatic Migrations

For Migrations, Entity Framework packs a set of commands which are executed from the Package Manager Console.

  1. Enable-Migrations

  • As the name suggests, it helps to enable Code First Migration for our project.

  1. Add-Migration

  • This commands helps to scaffold incremental migration of changes i.e. to add migrations that scans the model to find the changes after the last database updations.

  1. Update-Database

  • Performs the actual database updations based on the migration created with add-migration. In simple terms, to upgrade and downgrade database.

  1. Get-Migrations

  • Gets the list of migrations applied to the database.

To understand how migrations work, let’s build a simple domain model Employee-Department objects. Refer below,

 

public class Employee
{
    //Table properties
    [Key]
    public int EmployeeId { get; set; }
    public int DepartmentId { get; set; }

    [MaxLength(50)]
    public string FirstName { get; set; }
    [MaxLength(50)]
    public string LastName { get; set; }
    [MaxLength(50)]
    public string Address1 { get; set; }
    [MaxLength(50)]
    public string Address2 { get; set; }

    [MaxLength(50)]
    public string City { get; set; }

    [MaxLength(50)]
    public string State { get; set; }

    [MaxLength(50)]
    public string Country { get; set; }

    [MaxLength(10)]
    public string PostalCode { get; set; }

    [MaxLength(100)]
    public string Email { get; set; }

    public DateTime? DOB { get; set; }

    public virtual Department Department { get; set; }
}

public class Department
{
    [Key]
    public int DepartmentId { get; set; }
    [MaxLength(50)]
    public string DepartmentName { get; set; }

    public virtual ICollection<Employee> Employees { get; set; }
}
 

And the Context class below,

 

public class DBModel : DbContext
{
    public DBModel()
        : base("name=DBModel")
    {
    }
    public DbSet<Department> Departments { get; set; }
    public DbSet<Employee> Employees { get; set; }
}
 

Connection String

<add name="DBModel" providerName="System.Data.SqlClient" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=EFCodeFirstMigrationDemo;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\EFCodeFirstDemo.mdf" />

 

Using Code First Migrations

Code Based Migration

This is the recommended approach to manage database changes when using Entity Framework Code First.

STEP 1 – Enable Migrations

We need to first enable code first migration in our project. To do this, go to Tools> Nuget Package Manager > Package Manager Console. Execute the command Enable-Migrations. Refer below,

PM> enable-migrations

Checking if the context targets an existing database...

Code First Migrations enabled for project GridDemo.

PM>

This will add a folder Migrations in the solution and add a class called Configuration.cs class. Refer below,

 

internal sealed class Configuration : DbMigrationsConfiguration<GridDemo.Models.DL.DBModel>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = false;
    }

    protected override void Seed(GridDemo.Models.DL.DBModel 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. E.g.
        //
        //    context.People.AddOrUpdate(
        //      p => p.FullName,
        //      new Person { FullName = "Andrew Peters" },
        //      new Person { FullName = "Brice Lambson" },
        //      new Person { FullName = "Rowan Miller" }
        //    );
        //            
    }
}
 

The Seed() method will get executed after database creation to add initial data.

Note - It will also add another class file with format [timestamp]_InitialCreate.cs if you have already created a database from Code First model using database initializer by scaffolding the existing database.

STEP 2 – CREATE Initial Migration

Let’s now add our first migration that creates the initial database structure. Execute the command Add-Migrations [Migration name] for this.

 

PM> Add-Migration InitialDBCreate

Scaffolding migration 'InitialDBCreate'.

The Designer Code for this migration file includes a snapshot of your current Code First model. This snapshot is used to calculate the changes to your model when you scaffold the next migration. If you make additional changes to your model that you want to include in this migration, then you can re-scaffold it by running 'Add-Migration InitialDBCreate' again.

PM>

This will add a new migration class file Timestamp_IntialDBCreate.cs as below.

And the content of class file as,

 

public partial class InitialDBCreate : DbMigration
{
    public override void Up()
    {
        CreateTable(
            "dbo.Departments",
            c => new
                {
                    DepartmentId = c.Int(nullable: false, identity: true),
                    DepartmentName = c.String(maxLength: 50),
                })
            .PrimaryKey(t => t.DepartmentId);
        
        CreateTable(
            "dbo.Employees",
            c => new
                {
                    EmployeeId = c.Int(nullable: false, identity: true),
                    DepartmentId = c.Int(nullable: false),
                    FirstName = c.String(maxLength: 50),
                    LastName = c.String(maxLength: 50),
                    Address1 = c.String(maxLength: 50),
                    Address2 = c.String(maxLength: 50),
                    City = c.String(maxLength: 50),
                    State = c.String(maxLength: 50),
                    Country = c.String(maxLength: 50),
                    PostalCode = c.String(maxLength: 10),
                    Email = c.String(maxLength: 100),
                    DOB = c.DateTime(),
                })
            .PrimaryKey(t => t.EmployeeId)
            .ForeignKey("dbo.Departments", t => t.DepartmentId, cascadeDelete: true)
            .Index(t => t.DepartmentId);
        
    }
    
    public override void Down()
    {
        DropForeignKey("dbo.Employees", "DepartmentId", "dbo.Departments");
        DropIndex("dbo.Employees", new[] { "DepartmentId" });
        DropTable("dbo.Employees");
        DropTable("dbo.Departments");
    }
}
 

Every migration class will have 2 methods, Up() and Down(). As the name suggests, Up() gets called to upgrade and Down() gets called to downgrade.

STEP 3 – Update or Sync Database

Execute the command Update-Database to deploy the changes to database.

 

PM> update-database

Specify the '-Verbose' flag to view the SQL statements being applied to the target database.

Applying explicit migrations: [201705020924365_InitialDBCreate].

Applying explicit migration: 201705020924365_InitialDBCreate.

Running Seed method.

PM>

 

You can see the database created under App_Data Folder. Open Server Explorer and you can see the database as below,

Adding More Migrations

Now, let’s add another column in Employee model and add a new migration.

 

[MaxLength(10)]

public string Gender { get; set; }

 

To add this column into database, add a new migration using add-migration command like below,

 

PM> add-migration addEmployeeGender

Scaffolding migration 'addEmployeeGender'.

The Designer Code for this migration file includes a snapshot of your current Code First model. This snapshot is used to calculate the changes to your model when you scaffold the next migration. If you make additional changes to your model that you want to include in this migration, then you can re-scaffold it by running 'Add-Migration addEmployeeGender' again.

PM>

 

Executing the above command will add a new migration class in Migrations folder. The generated migration class will look like,

 

public partial class addEmployeeGender : DbMigration
{
    public override void Up()
    {
        AddColumn("dbo.Employees", "Gender", c => c.String());
    }
    
    public override void Down()
    {
        DropColumn("dbo.Employees", "Gender");
    }
}
 

To apply this migration, execute update-database command like below.

 

PM> update-database

Specify the '-Verbose' flag to view the SQL statements being applied to the target database.

No pending explicit migrations.

Running Seed method.

PM>

 

This will update the Employee table as seen below,

Adding Initial Data or Pre-Populate using Seed() Method

We can pre-populate data into the database table using Seed() method in Configuration class.

 

protected override void Seed(GridDemo.Models.DL.DBModel context)
{
    context.Departments.AddOrUpdate(d => d.DepartmentName,
        new Department() { DepartmentName = "IT" },
        new Department() { DepartmentName = "CSE" }
        );
    context.SaveChanges();
    context.Employees.AddOrUpdate(e => e.Email,
        new Employee()
        {
            FirstName = "Tom",
            LastName = "Hanks",
            Address1 = "1St Street",
            Address2 = "Columbia Ave",
            City = "Los Angeles",
            State = "CA",
            Country = "US",
            PostalCode = "123456",
            DepartmentId = context.Departments.Where(d => d.DepartmentName == "IT").First().DepartmentId,
            DOB = DateTime.Now.AddYears(-60),
            Email = "tom.hanks@gmail.com"
        },
        new Employee()
        {
            FirstName = "Bruce",
            LastName = "Willis",
            Address1 = "2nd Street",
            Address2 = "Farmers Ave",
            City = "Seattle",
            State = "WA",
            Country = "US",
            PostalCode = "756446",
            DepartmentId = context.Departments.Where(d => d.DepartmentName == "CSE").First().DepartmentId,
            DOB = DateTime.Now.AddYears(-55),
            Email = "bruce.will@gmail.com"
        },
        //Removed for brewity
        );
    context.SaveChanges();
}

 

Let’s also add another column called IsPermanent to Employee table before creating a new migration.

 

public bool IsPermanent { get; set; }

 

Adding migration and Updating Database

 

PM> add-migration addEmployeeIsPermanent

Scaffolding migration 'addEmployeeIsPermanent'.

The Designer Code for this migration file includes a snapshot of your current Code First model. This snapshot is used to calculate the changes to your model when you scaffold the next migration. If you make additional changes to your model that you want to include in this migration, then you can re-scaffold it by running 'Add-Migration addEmployeeIsPermanent' again.

PM>

 

PM> update-database

Specify the '-Verbose' flag to view the SQL statements being applied to the target database.

No pending explicit migrations.

Running Seed method.

PM>

This will add the new column and pump the initial data into Employee and Department table. The final database in server explorer will look like,

You can view the data by clicking “Show Table Data” from server explorer.

 

The Migrations folder will include all the migrations we added similar to below,

 

Get All Applied Migrations

To get all the applied migrations,

 

PM> get-migrations

Retrieving migrations that have been applied to the target database.

201705020942242_addEmployeeIsPermanent

201705020928575_addEmployeeGender

201705020924365_InitialDBCreate

 

Downgrade Migrations

To downgrade migration to a specific version, we can use update-database command by specifying the version in -targetmigration switch like below,

 

PM> update-database -TargetMigration:addEmployeeGender

Specify the '-Verbose' flag to view the SQL statements being applied to the target database.

Reverting migrations: [201705020942242_addEmployeeIsPermanent].

Reverting explicit migration: 201705020942242_addEmployeeIsPermanent.

PM>

 

 

Using Automatic Migrations

In this migrations, the database changes are automatically applied to database when the model changes. To do this, we need to execute Enable-Migrations command with EnableAutomaticMigrations switch.

 

PM> Enable-Migrations –EnableAutomaticMigrations

Checking if the context targets an existing database...

Code First Migrations enabled for project GridDemo.

 

To apply the model changes, execute update-database command as we do for code based migrations. There are few changes which cannot be applied using automatic migrations like column rename etc. In this cases, we need to apply these changes using code based migrations.

Happy Learning!!