如何创建初始化程序来创建和迁移mysql数据库?

我一直在学习如何使用EF一个星期左右,并且我一直坚持创建/更新我的数据库的问题。 如果不存在,我可以创建一个初始化器来创建数据库:

static class Program { static void Main() { Database.SetInitializer(new GumpDatabaseInitializer()); .... class GumpDatabaseInitializer : CreateDatabaseIfNotExists { public GumpDatabaseInitializer() { } protected override void Seed(GumpDatabase context) { context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX Name ON Stations (Name)"); // Other stuff } } 

或者我可以创建配置来迁移数据库

 static class Program { static void Main() { Database.SetInitializer(new MigrateDatabaseToLatestVersion()); .... internal sealed class Configuration : DbMigrationsConfiguration { public Configuration() { AutomaticMigrationsEnabled = true; SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator()); } protected override void Seed(GumpDatabase context) { } 

每个都正常工作,但我还没有想出办法做到这两点。 我可以通过更改SetInitializer调用在两个初始值设定项之间切换,但是如果我想创建数据库,如果它不存在,并且如果它是我该怎么做的话还要迁移它? 我需要创建自定义初始化程序吗?

谢谢

根据NSGaga答案编辑

 class CreateOrMigrateDatabaseInitializer : CreateDatabaseIfNotExists, IDatabaseInitializer where TContext : DbContext where TConfiguration : DbMigrationsConfiguration, new() { private readonly DbMigrationsConfiguration _configuration; public CreateOrMigrateDatabaseInitializer() { _configuration = new TConfiguration(); } public CreateOrMigrateDatabaseInitializer(string connection) { Contract.Requires(!string.IsNullOrEmpty(connection), "connection"); _configuration = new TConfiguration { TargetDatabase = new DbConnectionInfo(connection) }; } void IDatabaseInitializer.InitializeDatabase(TContext context) { Contract.Requires(context != null, "context"); if (context.Database.Exists()) { if (!context.Database.CompatibleWithModel(throwIfNoMetadata: false)) { var migrator = new DbMigrator(_configuration); migrator.Update(); } } else { context.Database.Create(); Seed(context); context.SaveChanges(); } } protected virtual void Seed(TContext context) { } } 

 internal sealed class Configuration : DbMigrationsConfiguration { public Configuration() { AutomaticMigrationsEnabled = true; AutomaticMigrationDataLossAllowed = false; SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator()); } protected override void Seed(GumpDatabase context) { } } 

 class GumpDatabaseInitializer : CreateOrMigrateDatabaseInitializer { public GumpDatabaseInitializer() { } protected override void Seed(GumpDatabase context) { context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX Name ON Stations (Name)"); context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX Name ON Sequences (Name)"); context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX StationPartNumber ON StationPartNumbers (StationId,PartNumberId)"); } } 

最后

 static void Main() { Database.SetInitializer(new GumpDatabaseInitializer()); 

我认为你几乎就在那里 – 你可以查找MigrateDatabaseToLatestVersion的源代码(它的开源http://entityframework.codeplex.com/ ) – 它非常简单,它的function就是调用DbMigrator – 就其而言我能看见。

所有你需要做的似乎是合并两个 – 使用一个或另一个作为基础,在那里添加其他function – 这应该工作正常我认为。

 class CreateAndMigrateDatabaseInitializer : CreateDatabaseIfNotExists, IDatabaseInitializer where TContext : DbContext where TConfiguration : DbMigrationsConfiguration, new() { private readonly DbMigrationsConfiguration _configuration; public CreateAndMigrateDatabaseInitializer() { _configuration = new TConfiguration(); } public CreateAndMigrateDatabaseInitializer(string connection) { Contract.Requires(!string.IsNullOrEmpty(connection), "connection"); _configuration = new TConfiguration { TargetDatabase = new DbConnectionInfo(connection) }; } void IDatabaseInitializer.InitializeDatabase(TContext context) { Contract.Requires(context != null, "context"); var migrator = new DbMigrator(_configuration); migrator.Update(); // move on with the 'CreateDatabaseIfNotExists' for the 'Seed' base.InitializeDatabase(context); } protected override void Seed(TContext context) { } } 

像这样称呼它……

 Database.SetInitializer(new CreateAndMigrateDatabaseInitializer()); 

…实际上,覆盖它(因为它是通用实现),就像你为CreateDatabaseIfNotExists做的那样(你只需要配置额外的’param’) – 并且只提供’种子’。

 class GumpDatabaseInitializer : CreateAndMigrateDatabaseInitializer { protected override void Seed(GumpDatabase context) { context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX Name ON Stations (Name)"); } } 

……并称之为

 Database.SetInitializer(new GumpDatabaseInitializer()); 

编辑:根据评论 – DbMigrator不应该运行两次。 它总是检查(花费一点时间)并进行“空白”更新并继续前进。 但是,如果您想要在进入之前删除它并“检查” – 这应该有效(更改上面的类似部分)……

 var migrator = new DbMigrator(_configuration); if (!context.Database.CompatibleWithModel(throwIfNoMetadata: false)) if (migrator.GetPendingMigrations().Any()) migrator.Update(); 

(这是一个冗余/双重检查 – 其中一个if-s应该足够了。在那里rest一下 – 看看究竟发生了什么,它不应该进入 – 一旦Db被迁移。正如我所提到的,我工作正常测试一下。

编辑:

用…替换InitializeDatabase的内部

 var doseed = !context.Database.Exists(); // && new DatabaseTableChecker().AnyModelTableExists(context); // check to see if to seed - we 'lack' the 'AnyModelTableExists' - could be copied/done otherwise if needed... var migrator = new DbMigrator(_configuration); // if (doseed || !context.Database.CompatibleWithModel(throwIfNoMetadata: false)) if (migrator.GetPendingMigrations().Any()) migrator.Update(); // move on with the 'CreateDatabaseIfNotExists' for the 'Seed' base.InitializeDatabase(context); if (doseed) { Seed(context); context.SaveChanges(); } 

如果迁移首先进行,这可以解决(中途)非播种问题。 迁移必须是第一个,否则你就会遇到问题。

你仍然需要正确地做到这一点 – 如果不是你可能需要的话,这就是要点 – 但如果有任何问题与MySQL等,可能还有一些腿部工作。

注意:如果你有一个数据库,仍然没有调用播种,但它是空的。 问题是混合两种不同的初始化器。 所以你必须解决这个问题 – 通过实现内部的Create …(我们无法调用的那个)或其他东西。

实际上它应该是:

 var migrator = new DbMigrator(_configuration); if (!context.Database.CompatibleWithModel(false) || migrator.GetPendingMigrations().Any()) migrator.Update(); 

因为如果我们有一个与我们的db模型无关的迁移,例如在我们的任何表中插入一行,就不会执行迁移。

要同时执行(种子和迁移),您实际上只需要使用MigrateDatabaseToLatestVersion初始化程序进行迁移。 为上下文启用迁移时,将创建从DbMigrationsConfiguration派生的Configuration类,您可以覆盖Seed方法DbMigrationsConfiguration数据库设定种子。 请注意,此方法执行时数据库可能已包含种子数据,但AddOrUpdate扩展方法可以方便地帮助您在数据库中进行“upserts”。

与一些其他数据库初始化器的Seed方法相比,这是不同的,其中数据库仅在最初创建时播种。 但是,在使用迁移时,您可能希望在数据库更改时更改种子数据,并且使用MigrateDatabaseToLatestVersion可以实现此目的。

要将种子组合与迁移相结合,您必须在新项目中执行以下步骤:

  1. 创建具有关联实体的代码优先DbContext

  2. 在包管理器控制台中,执行命令Enable-Migrations

  3. Migrations文件夹中,使用Seed方法生成Configuration类。 您可以修改此方法以为数据库设定种子:

     protected override void Seed(MyContext context) { // Add two entities with name "Foo" and "Bar". context.MyEntities.AddOrUpdate( e => e.Name, new MyEntity { Name = "Foo" }, new MyEntity { Name = "Bar" } ); } 
  4. 您需要创建一个派生自MigrateDatabaseToLatestVersion的数据库初始化MigrateDatabaseToLatestVersion

     class MyContextInitializer : MigrateDatabaseToLatestVersion { } 

    您还必须通过在应用程序启动时调用Database.SetInitializer(new MyContextInitializer())或使用元素在App.config文件中配置初始化程序。

  5. 在生成的Configuration类的构造函数中,您可以启用自动迁移:

     public Configuration() { AutomaticMigrationsEnabled = true } 

    但是,在团队中,您可能不愿意这样做。 在这种情况下,您将必须创建初始迁移(除非它是在您执行Enable-Migrations时创建的)。 在包管理器中,执行命令Add-Migration InitialCreate 。 这将创建创建数据库所需的第一次迁移。

此时,您有一个带迁移的DbContext和一个Seed方法。

总结一下:启用迁移,使用MigrateDatabaseToLatestVersion初始化程序,并在启用迁移时生成的Configuration类中添加种子数据。

虽然MigrateDatabaseToLatestVersion实际上创建了数据库(如果它不存在),甚至允许您对其进行播种,但如果您已经有一个基于CreateDatabaseIfNotExists的工作解决方案和/或不想将其与测试存在种子数据进行复杂化,那么您可以只需使用下面的方法inheritance而不是CreateDatabaseIfNotExists

 public class CreateOrMigrateDatabaseInitializer : CreateDatabaseIfNotExists, IDatabaseInitializer where TContext : DbContext where TConfiguration : DbMigrationsConfiguration, new() { void IDatabaseInitializer.InitializeDatabase(TContext context) { if (context.Database.Exists()) { if (!context.Database.CompatibleWithModel(throwIfNoMetadata: false)) { var migrationInitializer = new MigrateDatabaseToLatestVersion(true); migrationInitializer.InitializeDatabase(context); } } base.InitializeDatabase(context); } } 

这是基于之前的答案和OP自己的解决方案。 这也适用于其他提供商,但我只测试了SQL Server。