2013-04-03 82 views
14

我一直在学习如何使用EF一个星期左右的时间,并且一直在创建/更新我的数据库的问题上停滞不前。我能够创建一个初始化创建数据库,如果它不存在:如何创建初始化器来创建和迁移mysql数据库?

static class Program 
{ 
    static void Main() 
    { 
     Database.SetInitializer<GumpDatabase>(new GumpDatabaseInitializer()); 
.... 

class GumpDatabaseInitializer : CreateDatabaseIfNotExists<GumpDatabase> 
{ 
    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<GumpDatabase>(new MigrateDatabaseToLatestVersion<GumpDatabase, Configuration>()); 
.... 

internal sealed class Configuration : DbMigrationsConfiguration<GumpDatabase> 
{ 
    public Configuration() 
    { 
     AutomaticMigrationsEnabled = true; 
     SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator()); 
    } 

    protected override void Seed(GumpDatabase context) 
    { 

    } 

每个正常工作,但我还没有想出找出一种方法来做到这一点。我可以通过更改SetInitializer调用来在两个初始化器之间切换,但是如果我想创建数据库(如果它不在那里),并且如果它是我该做的事情还迁移它?我是否需要创建自定义初始化程序?

感谢

基于NSGaga

编辑回答

class CreateOrMigrateDatabaseInitializer<TContext, TConfiguration> : CreateDatabaseIfNotExists<TContext>, IDatabaseInitializer<TContext> 
    where TContext : DbContext 
    where TConfiguration : DbMigrationsConfiguration<TContext>, 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<TContext>.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<GumpDatabase> 
{ 
    public Configuration() 
    { 
     AutomaticMigrationsEnabled = true; 
     AutomaticMigrationDataLossAllowed = false; 
     SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator()); 
    } 

    protected override void Seed(GumpDatabase context) 
    { 
    } 
} 

class GumpDatabaseInitializer : CreateOrMigrateDatabaseInitializer<GumpDatabase,Gump.Migrations.Configuration> 
{ 
    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<GumpDatabase>(new GumpDatabaseInitializer()); 

回答

16

我觉得你非常有 - 你可以查找为MigrateDatabaseToLatestVersion的源代码(它是开源的http://entityframework.codeplex.com/) - 这是很简单的,它的作用几乎被调用DbMigrator - 远正如我所见。

你所要做的似乎是合并两个使用一个或另一个作为基础,在其中添加其他功能 - 应该工作正常,我认为。

class CreateAndMigrateDatabaseInitializer<TContext, TConfiguration> : CreateDatabaseIfNotExists<TContext>, IDatabaseInitializer<TContext> 
    where TContext : DbContext 
    where TConfiguration : DbMigrationsConfiguration<TContext>, 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<TContext>.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<GumpDatabase, YourNamespace.Migrations.Configuration>()); 

...其实,覆盖它(因为它是通用实现)就像你在做的CreateDatabaseIfNotExists(你只需要额外的“PARAM”的配置) - 并提供'种子'。

class GumpDatabaseInitializer : CreateAndMigrateDatabaseInitializer<GumpDatabase, YourNamespace.Migrations.Configuration> 
{ 
    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应该足够了,在那里休息一下 - 看看到底发生了什么,不应该进入 - 一旦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 ...内部(我们不能调用的那个调用)或其他东西。

+0

你会碰巧知道从何处获得EF 4.3.1的源代码?微软网站只有5个或6个。 – Matt 2013-04-03 20:43:41

+0

我认为他们已经开始只为新的更新(即EF 6)“开源” - 但源应该是相同的 - 至少在这方面。如果你有'DbMigrator'这几乎是 – NSGaga 2013-04-03 20:53:18

+0

它看起来像有一些缺失的部分:'使用System.Data.Entity.Config; using System.Data.Entity.Internal; using System.Data.Entity.Resources; using System.Data.Entity.Utilities;'在EF 4.3.1中找不到。 – Matt 2013-04-03 21:10:12

1

其实应该是:

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

,因为如果我们有一个迁移,这不是关系到我们数据库的模型,例如将在我们的任何表中的一行,迁移将不会被执行。

1

要做到这一点(种子和迁移),你真的只需要使用MigrateDatabaseToLatestVersion初始值设定项的迁移。当为您的上下文启用迁移时,将创建一个ConfigurationDbMigrationsConfiguration派生的类,并且您可以覆盖Seed方法来为数据库创建种子。请注意,当执行此方法时,数据库可能已经包含种子数据,但AddOrUpdate扩展方法可方便地帮助您在数据库中创建“upserts”。

与一些其他数据库初始化程序的Seed方法相比,数据库只在最初创建时才被播种。但是,当您使用迁移时,您可能希望在数据库更改时更改种子数据,并使用MigrateDatabaseToLatestVersion可以实现这一点。

若要迁移相结合播种,你将不得不执行新的项目下列步骤:

  1. 创建代码优先DbContext与相关实体

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

  3. Migrations文件夹中,生成一个Configuration类,其中Seed方法。您可以修改此方法以种子数据库:

    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派生:通过调用Database.SetInitializer(new MyContextInitializer())

    class MyContextInitializer 
        : MigrateDatabaseToLatestVersion<MyContext, Migrations.Configuration> { } 
    

    您还必须配置初始化您的应用程序启动或使用<databaseInitializer/>元素在App.config文件中。

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

    public Configuration() { 
        AutomaticMigrationsEnabled = true 
    } 
    

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

此时你有一个DbContext与迁移和Seed方法。

因此总结一下:启用迁移,使用MigrateDatabaseToLatestVersion初始值设定项,并在启用迁移时生成的Configuration类中添加种子数据。

0

虽然MigrateDatabaseToLatestVersion没有实际创建的数据库,如果不存在的话,甚至可以让你的种子吧,如果你基于CreateDatabaseIfNotExists和/或不希望它与复杂的测试已经有了一个可行的解决方案

public class CreateOrMigrateDatabaseInitializer<TContext, TConfiguration> : CreateDatabaseIfNotExists<TContext>, IDatabaseInitializer<TContext> 
     where TContext : DbContext 
     where TConfiguration : DbMigrationsConfiguration<TContext>, new() 
    { 

     void IDatabaseInitializer<TContext>.InitializeDatabase(TContext context) 
     { 
      if (context.Database.Exists()) 
      { 
       if (!context.Database.CompatibleWithModel(throwIfNoMetadata: false)) 
       { 
        var migrationInitializer = new MigrateDatabaseToLatestVersion<TContext, TConfiguration>(true); 
        migrationInitializer.InitializeDatabase(context); 
       } 
      } 

      base.InitializeDatabase(context); 
     } 
    } 

这是基于以前的答案和OP自己的解决方案:种子数据的存在,你可以通过从而非CreateDatabaseIfNotExists继承使用下面的。这也应该与其他提供者一起工作,但我只使用SQL Server进行测试。