EF 4.3 Code-Based Migrations Walkthrough

 


The information in this post is out of date.

Visit msdn.com/data/ef for the latest information on current and past releases of EF.

For Code First Migrations see https://msdn.com/data/jj591621


 

We have released the first go-live release of our Code First Migrations work as part of Entity Framework 4.3.

This post will provide an overview of the functionality that is available inside of Visual Studio for interacting with migrations. We will focus on the code-based workflow for using migrations. In this workflow each change is written out to a code-based migration that can reside in your project.

There is a separate Automatic Migrations Walkthrough that shows how this same set of changes can be applied using a mixture of code-based and automatic migrations.

This post assumes you have a basic understanding of Code First, if you are not familiar with Code First then please complete the Code First Walkthrough.

 

Building an Initial Model & Database

Before we start using migrations we need a project and a Code First model to work with. For this walkthrough we are going to use the canonical Blog and Post model.

  1. Create a new MigrationsCodeDemo Console application.
    .

  2. Add the latest version of the EntityFramework NuGet package to the project.

    • Tools –> Library Package Manager –> Package Manager Console.
    • Run the ‘Install-Package EntityFramework’ command.
      .
  3. Add a Model.cs file with the code shown below. This code defines a single Blog class that makes up our domain model and a BlogContext class that is our EF Code First context.

     using System.Data.Entity;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.Data.Entity.Infrastructure;
    
    namespace MigrationsCodeDemo
    {
        public class BlogContext : DbContext
        {
            public DbSet<Blog> Blogs { get; set; }
        }
    
        public class Blog
        {
            public int BlogId { get; set; }
            public string Name { get; set; }
        }
    }
    
  4. Now that we have a model it’s time to use it to perform data access. Update the Program.cs file with the code shown below.

     using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace MigrationsCodeDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                using (var db = new BlogContext())
                {
                    db.Blogs.Add(new Blog { Name = "Another Blog " });
                    db.SaveChanges();
    
                    foreach (var blog in db.Blogs)
                    {
                        Console.WriteLine(blog.Name);
                    }
                }
            }
        }
    }
    
  5. Run your application and you will see that a MigrationsCodeDemo.BlogContext database is created on your local SQLEXPRESS instance.

    MigrationsCodeDemoDatabase

  

Enabling Migrations

It’s time to make some more changes to our model.

  1. Let’s introduce a Url property to the Blog class.

     public class Blog
    {
        public int BlogId { get; set; }
        public string Name { get; set; }
        public string Url { get; set; }
    }
    
  2. If you were to run the application again you would get an InvalidOperationException because the database no longer matches your model.

    ”The model backing the 'BlogContext' context has changed since the database was created. Consider using Code First Migrations to update the database ( https://go.microsoft.com/fwlink/?LinkId=238269
    ).” .

  3. As the exception suggests, it’s time to start using Code First Migrations. The first step is to enable migrations for our context.

    • Run the ‘Enable-Migrations’ command in Package Manager Console.

      . .

  4. This command has added a Migrations folder to our project. This new folder contains two files:

    • The Configuration class. This class allows you to configure how Migrations behaves for your context. For this walkthrough we will just use the default configuration.

      Because there is just a single Code First context in your project, Enable-Migrations has automatically filled in the context type that this configuration applies to.

    • An InitialCreate migration. This migration was generated because we already had Code First create a database for us, before we enabled migrations. The code in this scaffolded migration represents the objects that have already been created in the database. In our case that is the Blog table with a BlogId and Name columns.

 

Your First Migration

Code First Migrations has two commands that you are going to become familiar with.

  • Add-Migration will scaffold the next migration based on changes you have made to your model.
  • Update-Database will apply any pending changes to the database.
  1. We need to scaffold a migration to take care of the new Url property we have added. The Add-Migration command allows us to give these migrations a name, let’s just call ours AddBlogUrl.

    • Run the ‘Add-Migration AddBlogUrl’ command in Package Manager Console.

      .

  2. In the Migrations folder we now have a new AddBlogUrl migration. The migration filename is pre-fixed with a timestamp to help with ordering.

     namespace MigrationsCodeDemo.Migrations
    {
        using System.Data.Entity.Migrations;
    
        public partial class AddBlogUrl : DbMigration
        {
            public override void Up()
            {
                AddColumn("Blogs", "Url", c => c.String());
            }
    
            public override void Down()
            {
                DropColumn("Blogs", "Url");
            }
        }
    }
    
  3. We could now edit or add to this migration but everything looks pretty good. Let’s use Update-Database to apply this migration to the database.

    • Run the ‘Update-Database’ command in Package Manager Console.

      .

  4. Code First Migrations has now updated the MigrationsCodeDemo.BlogContext database to include the Url column in the Blogs table. 

    MigrationsCodeDemoDatabaseUpdated

 

Customizing Migrations

So far we’ve generated and run a migration without making any changes. Now let’s look at editing the code that gets generated by default.

  1. It’s time to make some more changes to our model, let’s introduce a Blog.Rating property and a new Post class.

     public class Blog
    {
        public int BlogId { get; set; }
        public string Name { get; set; }     
        public string Url { get; set; } 
        public int Rating { get; set; }
    
        public List<Post> Posts { get; set; }
    }
    
    public class Post
    {
        public int PostId { get; set; }
        [MaxLength(200)]
        public string Title { get; set; }
        public string Content { get; set; }
    
        public int BlogId { get; set; }
        public Blog Blog { get; set; }
    }  
    
  2. Let’s use the Add-Migration command to let Code First Migrations scaffold its best guess at the migration for us. We’re going to call this migration AddPostClass.

    • Run the ‘Add-Migration AddPostClass’ command in Package Manager Console.

      .

  3. Code First Migrations did a pretty good job of scaffolding these changes, but there are some things we might want to change:

    • First up, let’s add a unique index to Posts.Title column.

    • We’re also adding a non-nullable Blogs.Rating column, if there is any existing data in the table it will get assigned the CLR default of the data type for new column (Rating is integer, so that would be 0). But we want to specify a default value of 3 so that existing rows in the Blogs table will start with a decent rating.

      (These changes to the scaffolded migration are highlighted)

       namespace MigrationsCodeDemo.Migrations
      {
          using System.Data.Entity.Migrations;
      
          public partial class AddPostClass : DbMigration
          {
              public override void Up()
              {
                  CreateTable(
                      "Posts",
                      c => new
                          {
                              PostId = c.Int(nullable: false, identity: true),
                              Title = c.String(maxLength: 200),
                              Content = c.String(),
                              BlogId = c.Int(nullable: false),
                          })
                      .PrimaryKey(t => t.PostId)
                      .ForeignKey("Blogs", t => t.BlogId, cascadeDelete: true)
                      .Index(t => t.BlogId)
                      .Index(p => p.Title, unique: true);
      
                  AddColumn("Blogs", "Rating", c => c.Int(nullable: false, defaultValue: 3));
              }
      
              public override void Down()
              {
                  DropIndex("Posts", new[] { "BlogId" });
                  DropForeignKey("Posts", "BlogId", "Blogs");
                  DropColumn("Blogs", "Rating");
                  DropTable("Posts");
              }
          }
      }
      
  4. Our edited migration is looking pretty good, so let’s use Update-Database to bring the database up-to-date. This time let’s specify the –Verbose flag so that you can see the SQL that Code First Migrations is running.

    • Run the ‘Update-Database –Verbose’ command in Package Manager Console.

 

Data Motion / Custom SQL

So far we have looked at migration operations that don’t change or move any data, now let’s look at something that needs to move some data around. There is no native support for data motion yet, but we can run some arbitrary SQL commands at any point in our script.

  1. Let’s add a Post.Abstract property to our model. Later, we’re going to pre-populate the Abstract for existing posts using some text from the start of the Content column.

     public class Post
    {
        public int PostId { get; set; }
        [MaxLength(200)]
        public string Title { get; set; }
        public string Content { get; set; }
        public string Abstract { get; set; }     
    
        public int BlogId { get; set; }
        public Blog Blog { get; set; }
    }
    
  2. Let’s use the Add-Migration command to let Code First Migrations scaffold its best guess at the migration for us. We’re going to call this migration AddPostAbstract.

    • Run the ‘Add-Migration AddPostAbstract’ command in Package Manager Console.

      .

  3. The generated migration takes care of the schema changes but we also want to pre-populate the Abstract column using the first 100 characters of content for each post. We can do this by dropping down to SQL and running an UPDATE statement after the column is added.

     namespace MigrationsCodeDemo.Migrations
    {
        using System.Data.Entity.Migrations;
    
        public partial class AddPostAbstract : DbMigration
        {
            public override void Up()
            {
                AddColumn("Posts", "Abstract", c => c.String());
    
                Sql("UPDATE Posts SET Abstract = LEFT(Content, 100) WHERE Abstract IS NULL");
            }
    
            public override void Down()
            {
                DropColumn("Posts", "Abstract");
            }
        }
    }
    
  4. Our edited migration looks good, so let’s use Update-Database to bring the database up-to-date. We’ll specify the –Verbose flag so that we can see the SQL being run against the database.

    - Run the ‘Update-Database –Verbose’ command in Package Manager Console.
    

 

Migrate to a Specific Version (Including Downgrade)

So far we have always upgraded to the latest migration, but there may be times when you want upgrade/downgrade to a specific migration.

  1. Let’s say we want to migrate our database to the state it was in after running our AddBlogUrl migration. We can use the –TargetMigration switch to downgrade to this migration.

    • Run the ‘Update-Database –TargetMigration:"AddBlogUrl"’ command in Package Manager Console.

This command will run the Down script for our AddBlogAbstract and AddPostClass migrations.

If you want to roll all the way back to an empty database then you can use the Update-Database –TargetMigration:$InitialDatabase command.

 

Getting a SQL Script

If another developer wants these changes on their machine they can just sync once we check our changes into source control. Once they have our new migrations they can just run the Update-Database command to have the changes applied locally. However if we want to push these changes out to a test server, and eventually production, we probably want a SQL script we can hand off to our DBA.

  1. Let’s run the Update-Database command but this time we’ll specify the –Script flag so that changes are written to a script rather than applied. We’ll also specify a source and target migration to generate the script for. We want a script to go from an empty database ($InitialDatabase) to the latest version (migration “AddPostAbstract”).

    Note: If you don’t specify a target migration, Migrations will use the latest migration as the target.

    • Run the ‘Update-Database -Script -SourceMigration:$InitialDatabase -TargetMigration:"AddPostAbstract"’ command in Package Manager Console.

      .

  2. Code First Migrations will run the migration pipeline but instead of actually applying the changes it will write them out to a .sql file for you. Once the script is generated, it is opened for you in Visual Studio, ready for you to view or save.

 

Summary

In this walkthrough you saw how to scaffold, edit and run code-based migrations to upgrade and downgrade your database. You also saw how to get a SQL script to apply migrations to a database.

Rowan Miller

Program Manager

ADO.NET Entity Framework