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 }