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();
}
}