Code First Migrations: Beta 1 ‘No-Magic’ 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 fourth preview of our migrations story for Code First development; Code First Migrations Beta 1. This release includes a preview of the developer experience for incrementally evolving a database as your Code First model evolves over time.

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 ‘no-magic’ workflow for using migrations. In this workflow each change is written out to a code-based migration that resides in your project. There is a separate Code First Migrations: Beta 1 ‘With-Magic’ Walkthrough that shows how this same set of changes can be applied by making use of automatic migrations.

This post assumes you have a basic understanding of the Code First functionality that is included in EF 4.2, if you are not familiar with Code First then please complete the Code First Walkthrough.

 

Building an Initial Model

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 ‘Beta1Demo’ Console application
    .

  2. Add 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 class 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.

    Note that we are removing the IncludeMetadataConvention to get rid of that EdmMetadata table that Code First adds to our database. The EdmMetadata table is used by Code First to check if the current model is compatible with the database, which is redundant now that we have the ability to migrate our schema. It isn’t mandatory to remove this convention when using migrations, but one less magic table in our database is a good thing right!

     using System.Data.Entity;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.Data.Entity.Infrastructure;
    
    namespace Beta1Demo
    {
        public class BlogContext : DbContext
        {
            public DbSet<Blog> Blogs { get; set; }
    
            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
            }
        }
    
        public class Blog
        {
            public int BlogId { get; set; }
            public string Name { get; set; }
        }
    }
    

 

Installing Migrations

Now that we have a Code First model let’s get Code First Migrations and configure it to work with our context.

  1. Add the EntityFramework.Migrations NuGet package to the project

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

      .

  2. The EntityFramework.Migrations package has added a Migrations folder to our project. At the moment this folder just contains a single Configuration class, this class has also been opened for you to edit. This class allows you to configure how migrations behaves for your context. We’ll just edit the Configuration class to specify our BlogContext.

     namespace Beta1Demo.Migrations
    {
        using System;
        using System.Data.Entity;
        using System.Data.Entity.Migrations;
        using System.Linq;
    
        internal sealed class Configuration : DbMigrationsConfiguration<BlogContext>
        {
            public Configuration()
            {
                AutomaticMigrationsEnabled = false;
    
                // Seed data: 
                //   Override the Seed method in this class to add seed data.
                //    - The Seed method will be called after migrating to the latest version.
                //    - You can use the DbContext.AddOrUpdate() helper extension method to avoid creating
                //      duplicate seed data. E.g.
                //
                //          myContext.AddOrUpdate(c => c.FullName,
                //              new Customer { FullName = "Andrew Peters", CustomerNumber = 123 },
                //              new Customer { FullName = "Brice Lambson", CustomerNumber = 456 },
                //              new Customer { FullName = "Rowan Miller", CustomerNumber = 789 }
                //          );
                //
            }
        }
    }
    

Our 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 haven’t generated any migrations yet so this will be our initial migration that creates the first set of tables (in our case that’s just the Blogs table). We can call the Add-Migration command and Code First Migrations will scaffold a migration for us with it’s best guess at what we should do to bring the database up-to-date with the current model.

    The Add-Migration command allows us to give these migrations a name, let’s just call ours ‘MyFirstMigration’.

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

      .

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

     namespace Beta1Demo.Migrations
    {
        using System.Data.Entity.Migrations;
    
        public partial class MyFirstMigration : DbMigration
        {
            public override void Up()
            {
                CreateTable(
                    "Blogs",
                    c => new
                        {
                            BlogId = c.Int(nullable: false, identity: true),
                            Name = c.String(),
                        })
                    .PrimaryKey(t => t.BlogId);
    
            }
    
            public override void Down()
            {
                DropTable("Blogs");
            }
        }
    } . 
    
  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 created a Beta1Demo.BlogContext database on our local SQL Express instance. We could now write code that uses our BlogContext to perform data access against this database. 

    BlogContextDatabase 

 

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 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 it’s best guess at the migration for us. We’re going to call this migration ‘MySecondSetOfChanges’.

    • Run the ‘Add-Migration MySecondSetOfChanges’ 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 below:

       namespace Beta1Demo.Migrations
      {
          using System.Data.Entity.Migrations;
      
          public partial class MySecondSetOfChanges : 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", "BlogId");
                  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 just 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 it’s 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 Beta1Demo.Migrations
    {
        using System.Data.Entity.Migrations;
    
        public partial class AddPostAbstract : DbMigration
        {
            public override void Up()
            {
                AddColumn("Posts", "Abstract", c => c.String());
    
                Sql("UPDATE dbo.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 ‘MyFirstMigration’ migration. We can use the –TargetMigration switch to downgrade to this migration.

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

This command will run the Down script for our ‘AddBlogAbstract’ and ‘MySecondSetOfChanges’ migrations. If you want to roll all the way back to an empty database then you can use the Update-Database –TargetMigration:"0" command.

 

Getting a SQL Script

Now that we have performed a few iterations on our local database let’s look at applying those same changes to another database.

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. We’re just going to create a script to deploy to a second database on the local SQL Express instance. Add an App.config file to your project and include a ‘MySecondDatabase’ connection string.

     <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <connectionStrings>
        <add name="MySecondDatabase"
             providerName="System.Data.SqlClient"
             connectionString="Server=.\SQLEXPRESS;Database=AnotherDatabase;Trusted_Connection=True;"/>
      </connectionStrings>
    </configuration>
    
  2. Now let’s run the Update-Database command but this time we’ll specify the –TargetDatabase flag to use the connection string we just added to the configuration file. We’ll also specify the –Script flag so that changes are written to a script rather than applied.

    • Run the ‘Update-Database –TargetDatabase:"MySecondDatabase" –Script’ command in Package Manager Console

      .

  3. 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 that represents the pending changes to a database.

As always, we really want your feedback on what we have so far, so please try it out and let us know what you like and what needs improving.

Rowan Miller

Program Manager

ADO.NET Entity Framework