Entity Framework Migrations

I recently posted on my intentions to create a migration solution for Entity Framework Code First using Red Gate tools.  The initial alpha version 0.9 is now live on CodePlex.  Here is how you would use the product. 

You have to obtain a license to Red Gate SQL Comparison SDK.  If you simply would like to try my solution, just get 14 days trial and check out what it does for you.  In my opinion, $700 dollars is not that much money for the functionality you get.  But of course, if you are giving your product away, that is a lot of cash.

Next you have to create some classes to use.  Your table classes are the same as in any other code first project.  You context however, now needs to inherit from ExtendedDbContext from my project.

    public class ProductsContext : ExtendedDbContext
    {
       
public ProductsContext(string connectionString)
            :
base
(connectionString)
        {
 
        }
 
       
public DbSet<Product> Products { get; set
; }
 
       
public DbSet<DbVersion> Version { get; set
; }
 
       
protected override void OnModelCreating(DbModelBuilder
modelBuilder)
        {
           
            modelBuilder.Conventions.Remove<
IncludeMetadataConvention
>();
           
base
.OnModelCreating(modelBuilder);
            
        }

 

There is one requirement, you must have a constructor that takes connection string.  You would typically have something like that anyway, so this is a minor adjustment to make.

Next you have to decide how you are going to use version comparison.  I have an interface that you must implement INewVersionProvider<TContext>.  I provide two of those out of the box, which should be enough for you to get started.  I have AssemblyBasedNewVersionProvider and ModelBasedNewVersionProvider.  Frist one stores version of the assembly that contains your DbContext and uses it for comparison.  To trigger migration you have to bump up a version of your assembly before running or deploying.  Second one uses the same mechanism as Entity Framework, creating a model hash based on actual context definition and its tables.  There is one requirement you have to use to support versioning of migrations.,  You have to include DbVersion table which is part of my framework in your context.  You can see that above in this blog in my context definition. 

Next you have to implement MigratingInitializer.  I am including base class, making this process a breeze. 


using EFUtil.Migrations.SqlServer;
using
EFUtil.Migrations;
 

namespace
EFUtil.Tests.Source
{
   
public class ProductInitializer : MigratingInitializer<ProductsContext
>
    {
       
public ProductInitializer(INewVersionProvider<ProductsContext
> versionProivider)
            :
base
(versionProivider)
        {
 
        }
 
       
protected override void BeforeMigration(ProductsContext
context)
        {
 
        }
 
       
protected override void AfterMigration(ProductsContext
context)
        {
 
        }
    }
}

 

As you can see, I provide two hooks, before and after that will allow you to massage the data before the migration or after, including adding seed data.  You can use either context or manual queries. 

using EFUtil.Migrations;
using
EFUtil.Migrations.SqlServer;
using
System.Data.Entity.Infrastructure;
 

namespace
EFUtil.TestApplication
{
   
public class ProductInitializer : MigratingInitializer<ProductsContext
>
    {
       
public ProductInitializer(INewVersionProvider<ProductsContext
> versionProivider)
            :
base
(versionProivider)
        {
 
        }
 
       
protected override void BeforeMigration(ProductsContext
context)
        {
           
var adapter = context as IObjectContextAdapter
;
           
if
(context.Database.Exists())
                adapter.ObjectContext.ExecuteStoreCommand(
"Update Products Set ProductNumber = ProductNumber + ‘1’"
);
        }
 
       
protected override void AfterMigration(ProductsContext
context)
        {
           
var adapter = context as IObjectContextAdapter
;
            adapter.ObjectContext.ExecuteStoreCommand(
"Update Products Set ProductName = ProductNumber + ‘ name’"
);
        }
    }
}

 

In the example above I am firing SQL Queries, but I could have added rows to context’s tables directly and fired Save().

I am also including conventions, just like I promised.  You can read more about conventions in my posts on global conventions and attribute conventions.  I also created a follow up post here.  All the code to support conventions is now part of CodePlex project.  I felt it is necessary to formalize my efforts in order to make the functionality cleaner and include tests for it.

If you download source code, you will see unit test project.  Note: you have to run one test at a time because there is a timing issue related to dropping databases.  There is also a quick test bed project you could use to play with code.  it is called EFUtil.TestApplication.  You can just keep altering Project class and re-running the project to observe migrations in action.

This is initial version, and I already have plans to further develop the functionality.

Here is my road map for the project:

  • Publish beta releases once I have some feedback and a number of downloads without bugs reported.
  • I am planning to add
    • Index support
    • Default values support

Please let me know what you think about my efforts and provide some feedback.

4 Comments

  1. I looked at the source, and it seems you just started the project. Let me know what your plans are. I have some ideas on writing a someting from scratch so that I do not have to use RedGate, but I just do not have time.

Leave a Reply

Your email address will not be published. Required fields are marked *