Code First Migrations History Table Customization

The migrations history table is an Entity Framework system table used to store metadata describing the schema version history of one or more EF Code First models within a given database.

History table customization is a new feature allowing various aspects of the history table (such as table name, column facets etc.) to be configured. It is an advanced feature primarily intended to improve the Migrations experience with third-party providers (where the default history model configuration is not always ideal). Care needs to be taken when customizing the history model as it becomes possible to define a model that no longer properly supports the migrations runtime – EF does not currently check whether a customized history model is compatible with the runtime. Additionally, customization must happen before the first migration is applied and is not supported if the first migration is an auto-migration. Migrations will detect any changes to the history model subsequent to the initial code migration and throw an appropriate error.

Configuration is expressed in a way already familiar to Code First users: Deriving a “history” DbContext, overriding OnModelCreating and calling fluent APIs on the provided DbModelBuilder instance.

1.     Deriving a History Context

The first step in customizing the history table is to subclass System.Data.Entity.Migrations.History.HistoryContext – this is the special derived DbContext class that Migrations uses internally to interact with the history table:

public class MyHistoryContext : HistoryContext
{
    public MyHistoryContext(
        DbConnection existingConnection, 
bool contextOwnsConnection,
string defaultSchema) : base(existingConnection, contextOwnsConnection, defaultSchema) { } }

2.     Override OnModelCreating

Next, we can override OnModelCreating in the normal way in order to configure the history model:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);
 
    modelBuilder.Entity<HistoryRow>().ToTable("MyHistoryTable");
    modelBuilder.Entity<HistoryRow>()
.Property(h => h.MigrationId).HasColumnName("migration_id"); modelBuilder.Entity<HistoryRow>()
.Property(h => h.ContextKey).HasColumnName("context_key"); modelBuilder.Entity<HistoryRow>()
.Property(h => h.Model).HasColumnName("metadata"); }

Do I need to call base.OnModelCreating?

Normally, when overriding OnModelCreating, calling the base implementation is optional because it doesn’t actually have any implementation – it is a noop. However, in this case, the base implementation in HistoryContext actually performs the default history model configuration. It currently looks like this:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.HasDefaultSchema(_defaultSchema);
 
    modelBuilder.Entity<HistoryRow>().ToTable(TableName);
    modelBuilder.Entity<HistoryRow>().HasKey(
        h => new
                    {
                        h.MigrationId,
                        h.ContextKey
                    });
    modelBuilder.Entity<HistoryRow>()
.Property(h => h.MigrationId).HasMaxLength(255).IsRequired(); modelBuilder.Entity<HistoryRow>()
.Property(h => h.ContextKey).HasMaxLength(512).IsRequired(); modelBuilder.Entity<HistoryRow>()
.Property(h => h.Model).IsRequired().IsMaxLength(); modelBuilder.Entity<HistoryRow>()
.Property(h => h.ProductVersion).HasMaxLength(32).IsRequired(); }

So, calling base.OnModelCreating is required in this case, and we don’t have to worry about configuring the entire history model.

3.     Create an IHistoryContextFactory

Now, we need a way to tell EF to use our derived history context. To do that we need to implement a simple factory method: System.Data.Entity.Migrations.History.IHistoryContextFactory.Create:

public class MyHistoryContextFactory : IHistoryContextFactory
{
    public HistoryContext Create(
        DbConnection existingConnection, 
bool contextOwnsConnection,
string defaultSchema) { return new MyHistoryContext(
existingConnection, contextOwnsConnection, defaultSchema); } }

4.     Update Migrations Configuration

Finally, we can enable our history context factory by setting it in our derived DbMigrationsConfiguration:

public class Configuration : DbMigrationsConfiguration<MyContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = false;
        HistoryContextFactory = new MyHistoryContextFactory();
    }
}

Last edited Oct 3, 2012 at 9:03 PM by AndrewPeters, version 10

Comments

jlerman Oct 9, 2013 at 4:25 PM 
Can this spec be updated? I'm finding HistoryContext ctor takes only 2 params, no IHistoryContextFactory (seems to be on-the-fly creation with a func) etc. Maybe more changes too..

jjwilliams Jul 17, 2013 at 9:49 PM 
Jliu: Did you ever figure anything out with this? I am creating an OracleMigratorSqlGenerator and the issue I am running into is the creation of the "db.__MigrationHistory" table.

jliu Apr 9, 2013 at 10:26 PM 
After I commented the following lines, it seems to be working. I'm only interested in modifying the defaultSchema name. However, the defaultSchema name is forced to lower case. I want to make it uppercase to match the schema for other tables. Otherwise it's huge pain for an Oracle Db . Are there any way we can really control the schema name?
thanks
JL

//modelBuilder.Entity<HistoryRow>().ToTable("MyHistoryTable");
//modelBuilder.Entity<HistoryRow>()
// .Property(h => h.MigrationId).HasColumnName("migration_id");
//modelBuilder.Entity<HistoryRow>()
// .Property(h => h.ContextKey).HasColumnName("context_key");
//modelBuilder.Entity<HistoryRow>()
// .Property(h => h.Model).HasColumnName("metadata");

jliu Mar 20, 2013 at 7:26 PM 
Hi,
I'm using EF6 Alpha2. I followed the exact steps you described above to customize the MigrationHistory table. I got the following error message when run the migration on a brand new database. Everything works correctly if comment out 'HistoryContextFactory = new MyHistoryContextFactory();'
PM> Get-Migrations -verbose
Using StartUp project 'Cityworks.DataAccess'.
Using NuGet project 'Cityworks.DataAccess'.
Retrieving migrations that have been applied to the target database.
Target database is: 'CWDBNEW' (DataSource: HULULU\SQLEXPRESS, Provider: System.Data.SqlClient, Origin: Configuration).
Migrating the history system table is only supported for move table operations. Ensure all other history table configuration happens as part of an initial explicit migration.

thanks
jliu