共用方式為


EF 4.3 Beta 1: Automatic 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 Automatic Migrations see https://msdn.com/data/jj554735


 

We have released the final preview of the Code First Migrations work as part of Entity Framework 4.3 Beta 1.

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 workflow that combines automatic and code-based migrations. In this workflow most changes can be automatically calculated and applied. More complex changes are written out to code-based migrations that reside in your project.

There is a separate EF 4.3 Beta 1: Code-Based Migrations Walkthrough that shows how this same set of changes can be applied using purely code-based 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

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 MigrationsAutomaticDemo Console application.
    .

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

    • Tools –> Library Package Manager –> Package Manager Console.
    • Run the ‘Install-Package EntityFramework –IncludePrerelease’ 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.

     using System.Data.Entity;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.Data.Entity.Infrastructure;
    
    namespace MigrationsAutomaticDemo
    {
        public class BlogContext : DbContext
        {
            public DbSet<Blog> Blogs { get; set; }
        }
    
        public class Blog
        {
            public int BlogId { get; set; }
            public string Name { get; set; }
        }
    }
    
  

Enabling Migrations

Now that we have a Code First model, let’s enable Migrations to work with our context.

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

    .

  2. This command has added a Migrations folder to our project. At the moment this folder just contains a single Configuration class. The Configuration class allows you to configure how migrations behaves for your context.

    Because there is just a single Code First context in your project, Enable-Migrations has automatically filled in the context type in the base class and Seed method for you.
    We’ll just edit the Configuration class to enable automatic migrations (highlighted below).

     namespace MigrationsAutomaticDemo.Migrations
    {
        using System;
        using System.Data.Entity;
        using System.Data.Entity.Migrations;
        using System.Linq;
    
        internal sealed class Configuration : DbMigrationsConfiguration<MigrationsAutomaticDemo.BlogContext>
        {
            public Configuration()
            {
                AutomaticMigrationsEnabled = true;
            }
    
            protected override void Seed(MigrationsAutomaticDemo.BlogContext 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" }
                //    );
                //
            }
        }
    }
    

 

Our First Automatic Migration

Code First Migrations has two commands that you are going to become familiar with. Add-Migration will scaffold a code-based migration based on changes you have made to your model. Update-Database will apply any pending changes to the database. We are going to avoid using Add-Migration (unless we really need to) and focus on letting Code First Migrations automatically calculate and apply the changes.

  1. Let’s use Update-Database to get Code First Migrations to push our model to the database.

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

      .

  2. Code First Migrations has now created a MigrationsAutomaticDemo.BlogContext database on our local SQL Express instance. We could now write code that uses our BlogContext to perform data access against this database. 

    MigrationsAutomaticDemoDatabase

 

Our Second Automatic Migration

Let’s make another change and let Code First Migrations automatically push the changes to the database for us.

  1. Let’s introduce a new Post class.

     public class Blog
    {
        public int BlogId { get; set; }
        public string Name { 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 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.

.

Adding a Code Based Migration

Now let’s look at something we might want to use a code-based migration for.

  1. Let’s add a Blog.Rating property.

     public class Blog
    {
        public int BlogId { get; set; }
        public string Name { get; set; }
        public int Rating { get; set; }
    
    
        public List<Post> Posts { get; set; }
    }
    
  2. We could just run Update-Database to push these changes to the database. However, were 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.

    Let’s use the Add-Migration command to write this change out to a code-based migration so that we can edit it. The Add-Migration command allows us to give these migrations a name, let’s just call ours MyFirstCodeMigration.

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

      .

  3. In the Migrations folder we now have a new MyFirstCodeMigration migration. The migration is pre-fixed with a timestamp to help with ordering. Let’s edit the generated code to specify a default value of 3 for Blog.Rating.

    The migration also has a code-behind file that captures some metadata. This metadata will allow Code First Migrations to replicate the automatic migrations we performed before this code-based migration. This is important if another developer wants to run our migrations or when it’s time to deploy our application.

     namespace MigrationsAutomaticDemo.Migrations
    {
        using System.Data.Entity.Migrations;
    
        public partial class MyFirstCodeMigration : DbMigration
        {
            public override void Up()
            {
                AddColumn("Blogs", "Rating", c => c.Int(nullable: false, defaultValue: 3));
            }
    
            public override void Down()
            {
                DropColumn("Blogs", "Rating");
            }
        }
    }
    
  4. Our edited migration is looking pretty good, so let’s use Update-Database to bring the database up-to-date.

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

 

Back to Automatic Migrations

Fortunately we don’t have to keep using code-based migrations now, we can switch back to automatic migrations for our simpler changes. Code First Migrations will take care of performing the automatic and code-based migrations in the correct order based on the metadata it is storing in the code-behind file for each code-based migration.

  1. Let’s add a Post.Abstract property to our model.

     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 Update-Database to get Code First Migrations to push this change to the database using an automatic migration.

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

 

Data Motion / Custom SQL

So far we have just looked at migration operations that can leave all the data in place, 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.

We just added the Abstract column. Let’s pre-populate it for existing posts using text from the Content column.

  1. Use the Add-Migration command to let Code First Migrations add an empty migration for us. We’re going to call this migration PopulatePostAbstract.

    (The migration will be empty because there are no pending model changes that haven’t been applied to the database)

    • Run the ‘Add-Migration PopulatePostAbstract’ command in Package Manager Console.
  2. Update the migration to run some custom SQL that will populate the Abstract column.

     namespace MigrationsAutomaticDemo.Migrations
    {
        using System.Data.Entity.Migrations;
    
        public partial class PopulatePostAbstract : DbMigration
        {
            public override void Up()
            {
                Sql("UPDATE dbo.Posts SET Abstract = LEFT(Content, 100) WHERE Abstract IS NULL");
            }
    
            public override void Down()
            {
            }
        }
    }
    
  3. 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 MyFirstCodeMigration migration. We can use the –TargetMigration switch to downgrade to this migration. This is going to cause some columns that were added as part of an automatic migration to be dropped automatically on the way down. Code First Migrations won’t let this happen without you knowing about it, so we need to specify the –Force switch to acknowledge that we are OK with the potential data loss.

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

This command will run the Down script for our PopulatePostAbstract migration, then use the automatic pipeline to revert the addition of the Abstract column.

If you want to roll all the way back to an empty database then you can use the Update-Database –TagetMigration:"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. Now 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 migration to generate the script from. We want a script to go from an empty database (migration “0”) to the latest version.

    Note: You can also specify a target migration to generate a script to the database state at the end of a code-based migration. If you don’t specify a target migration, Migrations will use the latest version as the target (including any automatic migrations that have been applied since the last code-based migration).

    • Run the ‘Update-Database -Script -SourceMigration:"0"’ 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.

NOTE: There are a number of bugs in the scripting functionality in EF 4.3 Beta1 that prevent you generating a script starting from a migration other than an empty database. These bugs will be fixed in the final RTM.

 

Summary

In this walkthrough you saw how to use automatic migrations to push model changes to the database. You saw how to scaffold and run code-based migrations when you need more control. You also saw how to upgrade and downgrade your database. Finally we looked at how to get a SQL script that represents the pending changes to a database.

Rowan Miller

Program Manager

ADO.NET Entity Framework

Comments

  • Anonymous
    January 13, 2012
    The comment has been removed

  • Anonymous
    January 16, 2012
    @Matt Hidinger – Thanks for the feedback, our team is discussing this at the moment. We don’t want to make AddOrUpdate a method on IDbSet<T> itself, because we don’t feel it’s a common enough scenario to expose directly on DbSet<T>. We are looking at making the extension method on IDbSet<T> though and if your IDbSet<T> implementation has an AddOrUpdate method, we’ll call that.

  • Anonymous
    January 31, 2012
    Thanks for the great walkthrough. I am currently playing around with AppHarbor and am curious about something. How can I set this up to automatically upgrade the database each time my app is deployed there? At first I will be generating the scripts as you describe and applying them in SSMS. If that goes well for a while and I get comfortable with things, I'd just prefer to let AppHarbor handle it for me. TL;DR: can I configure my project to automatically step up/down as necessary when the app starts (without developer intervention)?

  • Anonymous
    January 31, 2012
    FYI in case others see this: I was getting this error trying to install the prerelease version of EF:    A parameter cannot be found that matches parameter name 'IncludePrerelease' I reinstalled Nuget via the VS Extension manager and the problem was solved.

  • Anonymous
    February 01, 2012
    The comment has been removed

  • Anonymous
    February 01, 2012
    What is the best way to sync up my POCO classes with my DB ?   They have gotten out of sync  due to some migrations failures...  How can I determine what the differences are and correct those differences so that I can once again use the migrations tool to keep them in sync ? Thanks Greg

  • Anonymous
    February 02, 2012
    @Avinash You need to upgrade your version of Nuget. Cheers, Andrew.

  • Anonymous
    February 02, 2012
    @Michael Check out the new MigrateDatabaseToLatestVersion DbContext Initializer. Cheers, Andrew.

  • Anonymous
    February 02, 2012
    @Greg, The easiest way is to use Add-Migration to scaffold the current delta between your classes and the database. Cheers, Andrew.

  • Anonymous
    February 02, 2012
    When the seed metod is being called all the IDbSet<T> from the context are null

  • Anonymous
    February 05, 2012
    I'm using IDBSet in my DBContext derived class for my collections. Migrations does not seem to pick up on this, since the migrations are empty (except for some metadata).

  • Anonymous
    February 09, 2012
    @Andrew Thanks!