WPF Entity Framework Code First migrations

Recently I wrote a WPF application using Entity Framework Code First and released it into the wild.  Shortly after, the business requirements changed and I had to make changes to the model.  Thus, I was introduced to Code First migrations.

I did a lot of research on Code First migrations and could only really find the most simple of examples, and almost robotic documentation on MSDN that provided minimal help. This post aims to provide a clearer view on migrations, and how to properly create them.

Before we can start with migrations, we need to create our normal C# code and then add in Entity Framework using NuGet.  Once done, we can create our first migration and then explore a second migration.

The Code

The code is going to revolve around the common idea of customers and orders.

C# Code Structure

Code structure; An Order has a collection of Product and a Customer, the Customer has a CustomerName. Any properties that are marked as virtual are navigation properties, and are lazy loaded by Entity Framework at runtime.

public class Customer
{
    public int Id { get; set; }
    public int CustomerNameId { get; set; }
    public virtual CustomerName CustomerName { get; set; }
}

public class CustomerName
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class Product
{
    public int Id { get; set; }
    public string Description { get; set; }
    public decimal Price { get; set; }
}

public class Order
{
    public int Id { get; set; }
    public int CustomerId { get; set; }
    public virtual Customer Customer { get; set; }
    public virtual List<Product> Products { get; set; }
    public DateTime Placed { get; set; }
}

With the code structure in place, we can now introduce Entity Framework. Go ahead and use the package manager console, or the library package manager to install Entity Framework.

install-package EntityFramework

Enabling Migrations

Before enabling migrations, ensure that you have a DbContext class that contains a DbSet for use throughout the application;

public class Context : DbContext
{
    public DbSet<Order> Orders { get; set; }
}

Entity Framework uses the DbContext class to determine the structure of your database.

The key to making migrations work properly is to create an initial migration that will set up your database into its initial state when you deploy the application.

In the package manager console window, type the following command;

Enable-Migrations

You will notice that this process has has created a new folder (Migrations), and two new files; (Configuration.cs and InitialCreate, prefixed with a Date/Time stamp)

Entity Framework Initial Create

 

 

A word about seed data

At the time of writing, the current version of Entity Framework is 5.0.0.0 (public release). I have found that seed data does not behave in the way that I expect.

Open Configuration.cs and observe the seed data method;

protected override void Seed(CodeFirstMigrations.Context context)
{
    //  This method will be called after migrating to the latest version.

<pre><code>//  You can use the DbSet&amp;lt;T&amp;gt;.AddOrUpdate() helper extension method 
//  to avoid creating duplicate seed data. E.g.
//
//    context.People.AddOrUpdate(
//      p =&amp;gt; p.FullName,
//      new Person { FullName = &amp;quot;Andrew Peters&amp;quot; },
//      new Person { FullName = &amp;quot;Brice Lambson&amp;quot; },
//      new Person { FullName = &amp;quot;Rowan Miller&amp;quot; }
//    );
//
</code></pre>

}

Note that the comment clearly states that the method will be called directly after migrating to the latest version. I’ve found that, in reality, this method is called every time your application starts up. I suppose this is why the AddOrUpdate extension method was added, to prevent duplicate seed data.

Analysing the Visual Studio IntelliSense documentation closely, its pretty clear what this method expects for the first parameter;

AddOrUpdate Extension Method

CLICK IMAGE TO ENLARGE

For the AddOrUpdate method, we must pass in a parameter that will determine if the seed data is ADDED or UPDATED. Unfortunately, this doesn’t seem to work when passing in the primary key of the table (the Id property). For me, this is a major limitation and hopefully it will be resolved in future releases.

The solution here is to skip the seed data method altogether, and add some logic in the application start-up method to determine if the seed data needs to be added or updated.

The final step is to update your initializer to use the MigrateDatabaseToLatestVersion database initializer;

public class Initializer : MigrateDatabaseToLatestVersion<Context, Configuration>
{
}

You can now deploy your application to your customers, knowing that when the customer runs the program for the first time, the database will be created as you expect and and seed data will also be added.

Your second migration

Your second migration now should be straight forward. Simply go ahead and make the desired changes to your model. We will add the following class;

[Table("CreditCardInformation")]
public class CreditCardInformation
{
    public int Id { get; set; }
    public CreditCardType CreditCardType { get; set; }
    public string CreditCardNumber { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
}

Note that the Table attribute used above is to ensure that Entity Frameworks pluralisation service doesn’t incorrectly name our table. Add a CreditCardInformation property to Customer.

public class Customer
{
    public int Id { get; set; }
    public int CustomerNameId { get; set; }
    public virtual CustomerName CustomerName { get; set; }
    public virtual CreditCardInformation CreditCardInformation { get; set; }
}

Now you are ready to create a migration. You can name the migration simply by passing the desired name in as a parameter.

Add-Migration AddCreditCardInformation

Your migration will look something like this;

public partial class AddCreditCardInformation : DbMigration
{
    public override void Up()
    {
        CreateTable(
            &quot;dbo.CreditCardInformation&quot;,
            c =&gt; new
                {
                    Id = c.Int(nullable: false, identity: true),
                    CreditCardType = c.Int(nullable: false),
                    CreditCardNumber = c.String(),
                    StartDate = c.DateTime(nullable: false),
                    EndDate = c.DateTime(nullable: false),
                })
            .PrimaryKey(t =&gt; t.Id);

<pre><code>    AddColumn(&amp;quot;dbo.Customers&amp;quot;, &amp;quot;CreditCardInformation_Id&amp;quot;, c =&amp;gt; c.Int());
    AddForeignKey(&amp;quot;dbo.Customers&amp;quot;, &amp;quot;CreditCardInformation_Id&amp;quot;, &amp;quot;dbo.CreditCardInformation&amp;quot;, &amp;quot;Id&amp;quot;);
    CreateIndex(&amp;quot;dbo.Customers&amp;quot;, &amp;quot;CreditCardInformation_Id&amp;quot;);
}

public override void Down()
{
    DropIndex(&amp;quot;dbo.Customers&amp;quot;, new[] { &amp;quot;CreditCardInformation_Id&amp;quot; });
    DropForeignKey(&amp;quot;dbo.Customers&amp;quot;, &amp;quot;CreditCardInformation_Id&amp;quot;, &amp;quot;dbo.CreditCardInformation&amp;quot;);
    DropColumn(&amp;quot;dbo.Customers&amp;quot;, &amp;quot;CreditCardInformation_Id&amp;quot;);
    DropTable(&amp;quot;dbo.CreditCardInformation&amp;quot;);
}
</code></pre>

}

Entity Framework gives us the ability to roll back a migration if needed. When a roll back occurs, the Down method on the migration is called and the changes are reverted.

To perform a rollback, simply run the following command;

Update-Database -TargetMigration:"NameOfMigrationToRollBackTo"

If you try to run the application at this time, you will get the following error message;

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

This is because you need to commit the changes to your database, you can do this using the following command;

Update-Database

Run the application again, and all should work as you expect.

Summary

Entity Framework Code First migrations are an exciting and powerful new feature that will dramatically simplify your deployment and update process. The MigrateDatabaseToLatestVersion database initialization strategy will take care of automatically updating your database to the most current version, and can even help with seed data. Seed data is made easy thanks to a new AddOrUpdate extension method, but is limited in that you cannot use the table primary key as the identifier.