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;
///
/// 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.
///
public class DbSeederMigrator : 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";
///
/// Initializes a new instance of the DbMigrator class.
///
/// Configuration to be used for the migration process.
public DbSeederMigrator(DbMigrationsConfiguration configuration)
: base(configuration)
{ }
///
/// Migrates the database to the latest version
///
public void MigrateToLatestVersion()
{
var seedList = new List>();
// 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);
}
}
///
/// Gets the IMigrationDataSeeder from the DbMigration if the interface was implemented
///
/// The instance of the migration to seed
/// The migration instance typed as IMigrationDataSeeder
private IMigrationDataSeeder GetSeederFromMigration(DbMigration migration)
{
return migration as IMigrationDataSeeder;
}
///
/// Creates a full type instance for the migration id by using the current migrations namespace
/// ie: Project.Core.Data.Migrations.34589734533_InitialCreate
///
/// The migrator context
/// The migration id from the migrations list of the migrator
/// The full DbMigration instance
private DbMigration GetMigrationFromMigrationId(string migrationId)
{
string migrationTypeName =
string.Format(_migrationTypeFormat,
Configuration.MigrationsNamespace,
GetMigrationClassName(migrationId),
Configuration.MigrationsAssembly.FullName);
return CreateTypeInstance(migrationTypeName);
}
///
/// Creates a new instance of a typename
///
/// The type of the return instance
/// The full name (including assembly and namespaces) of the type to create
///
/// A new instance of the type if it is (or boxable to) ,
/// otherwise the default of
///
private TType CreateTypeInstance(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"
///
/// Checks if the migration id is valid
///
/// The migration id from the migrations list of the migrator
/// true if valid, otherwise false
///
/// This snippet has been copied from the EntityFramework source (http://entityframework.codeplex.com/)
///
private bool IsValidMigrationId(string migrationId)
{
if (string.IsNullOrWhiteSpace(migrationId))
return false;
return _migrationIdPattern.IsMatch(migrationId)
|| migrationId == DbMigrator.InitialDatabase;
}
///
/// Checks if the the migration id belongs to an automatic migration
///
/// The migration id from the migrations list of the migrator
/// true if automatic, otherwise false
///
/// This snippet has been copied from the EntityFramework source (http://entityframework.codeplex.com/)
///
private bool IsAutomaticMigration(string migrationId)
{
if (string.IsNullOrWhiteSpace(migrationId))
return false;
return migrationId.EndsWith(_automaticMigration, StringComparison.Ordinal);
}
///
/// Gets the ClassName from a migration id
///
/// The migration id from the migrations list of the migrator
/// The class name for this migration id
///
/// This snippet has been copied from the EntityFramework source (http://entityframework.codeplex.com/)
///
private string GetMigrationClassName(string migrationId)
{
if (string.IsNullOrWhiteSpace(migrationId))
return string.Empty;
return migrationId.Substring(16);
}
#endregion
}