Skip to content

Instantly share code, notes, and snippets.

@florisrobbemont
Created August 4, 2012 15:27
Show Gist options
  • Save florisrobbemont/3258344 to your computer and use it in GitHub Desktop.
Save florisrobbemont/3258344 to your computer and use it in GitHub Desktop.
EF: Bundle Seed code with Migration classes for seeding per migration
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Migrations;
using System.Linq;
using System.Text.RegularExpressions;
/// <summary>
/// Provides advanced migrations by providing a seeding platform for each migration.
/// This allows for initial seed data after each new database version (for example when
/// deploying new features and you want to include initial data). Seeders will be executing
/// in the correct order after all migrations have been completed.
/// </summary>
public class DbSeederMigrator<TContext> : DbMigrator where TContext : DbContext, new()
{
private static readonly Regex _migrationIdPattern = new Regex(@"\d{15}_.+");
private const string _migrationTypeFormat = "{0}.{1}, {2}";
private const string _automaticMigration = "AutomaticMigration";
/// <summary>
/// Initializes a new instance of the DbMigrator class.
/// </summary>
/// <param name="configuration">Configuration to be used for the migration process.</param>
public DbSeederMigrator(DbMigrationsConfiguration configuration)
: base(configuration)
{ }
/// <summary>
/// Migrates the database to the latest version
/// </summary>
public void MigrateToLatestVersion()
{
var seedList = new List<IMigrationDataSeeder<TContext>>();
// Apply migrations
foreach (var migrationId in GetPendingMigrations())
{
if (IsAutomaticMigration(migrationId))
continue;
if(!IsValidMigrationId(migrationId))
continue;
var migration = GetMigrationFromClassName(migrationId);
var migrationSeeder = GetSeederFromMigration(migration);
// Call the actual update to execute this migration
base.Update(migrationId);
if (migrationSeeder != null)
seedList.Add(migrationSeeder);
}
// Create a new datacontext using the generic type provided
TContext databaseContext = new TContext();
// Apply data seeders
foreach (var migrationSeeder in seedList)
{
migrationSeeder.Seed(databaseContext);
}
}
/// <summary>
/// Gets the IMigrationDataSeeder from the DbMigration if the interface was implemented
/// </summary>
/// <param name="migration">The instance of the migration to seed</param>
/// <returns>The migration instance typed as IMigrationDataSeeder</returns>
private IMigrationDataSeeder<TContext> GetSeederFromMigration(DbMigration migration)
{
return migration as IMigrationDataSeeder<TContext>;
}
/// <summary>
/// Creates a full type instance for the migration id by using the current migrations namespace
/// ie: Project.Core.Data.Migrations.34589734533_InitialCreate
/// </summary>
/// <param name="migrator">The migrator context</param>
/// <param name="migrationId">The migration id from the migrations list of the migrator</param>
/// <returns>The full DbMigration instance</returns>
private DbMigration GetMigrationFromMigrationId(string migrationId)
{
string migrationTypeName =
string.Format(_migrationTypeFormat,
Configuration.MigrationsNamespace,
GetMigrationClassName(migrationId),
Configuration.MigrationsAssembly.FullName);
return CreateTypeInstance<DbMigration>(migrationTypeName);
}
/// <summary>
/// Creates a new instance of a typename
/// </summary>
/// <typeparam name="TType">The type of the return instance</typeparam>
/// <param name="typeName">The full name (including assembly and namespaces) of the type to create</param>
/// <returns>
/// A new instance of the type if it is (or boxable to) <typeparamref name="TType"/>,
/// otherwise the default of <typeparamref name="TType"/>
/// </returns>
private TType CreateTypeInstance<TType>(string typeName) where TType : class
{
Type classType = Type.GetType(typeName, false);
if (classType == null)
return default(TType);
object newType = Activator.CreateInstance(classType);
return newType as TType;
}
#region "Migration ID validation"
/// <summary>
/// Checks if the migration id is valid
/// </summary>
/// <param name="migrationId">The migration id from the migrations list of the migrator</param>
/// <returns>true if valid, otherwise false</returns>
/// <remarks>
/// This snippet has been copied from the EntityFramework source (http://entityframework.codeplex.com/)
/// </remarks>
private bool IsValidMigrationId(string migrationId)
{
if (string.IsNullOrWhiteSpace(migrationId))
return false;
return _migrationIdPattern.IsMatch(migrationId)
|| migrationId == DbMigrator.InitialDatabase;
}
/// <summary>
/// Checks if the the migration id belongs to an automatic migration
/// </summary>
/// <param name="migrationId">The migration id from the migrations list of the migrator</param>
/// <returns>true if automatic, otherwise false</returns>
/// <remarks>
/// This snippet has been copied from the EntityFramework source (http://entityframework.codeplex.com/)
/// </remarks>
private bool IsAutomaticMigration(string migrationId)
{
if (string.IsNullOrWhiteSpace(migrationId))
return false;
return migrationId.EndsWith(_automaticMigration, StringComparison.Ordinal);
}
/// <summary>
/// Gets the ClassName from a migration id
/// </summary>
/// <param name="migrationId">The migration id from the migrations list of the migrator</param>
/// <returns>The class name for this migration id</returns>
/// <remarks>
/// This snippet has been copied from the EntityFramework source (http://entityframework.codeplex.com/)
/// </remarks>
private string GetMigrationClassName(string migrationId)
{
if (string.IsNullOrWhiteSpace(migrationId))
return string.Empty;
return migrationId.Substring(16);
}
#endregion
}
public partial class InitialCreate : DbMigration, IMigrationDataSeeder<DataContext>
{
public override void Up(){
// Some migration Code
}
public override void Down(){
// Some migration Code
}
public void Seed(DataContext context)
{
// Some seed code, the same as you would do in the Configuration Seed method
context.SaveChanges();
}
}
/// <summary>
/// Provides data seeding capabities to the EF Migration classes
/// </summary>
public interface IMigrationDataSeeder<TContext> where TContext : DbContext
{
/// <summary>
/// Seeds this migration
/// </summary>
void Seed(TContext context);
}
var configuration = new Configuration();
var migrator = new DbSeederMigrator<DataContext>(configuration);
migrator.MigrateToLatestVersion();
@prabhakarreddy1234
Copy link

I am not sure where to create an instance of Configuration. I think this will be done by EF itself. How can i intercept this in my code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment