【问题标题】:How to create initializer to create and migrate mysql database?如何创建初始化程序来创建和迁移 mysql 数据库?
【发布时间】:2013-04-03 19:20:08
【问题描述】:

我已经学习如何使用 EF 大约一个星期了,但我一直被困在创建/更新我的数据库的问题上。如果数据库不存在,我可以创建一个初始化程序来创建数据库:

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());

【问题讨论】:

    标签: c# mysql migration entity-framework-4.3 initializer


    【解决方案1】:

    我想你已经差不多了 - 你可以查找 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 所做的那样覆盖它(因为它是通用实现)(您只需为配置提供额外的“参数”) - 只需提供“种子”。

    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());
    

    编辑: 基于 cmets - 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 等有关的任何问题,可能需要更多的工作。

    注意:如果您有 db,仍然不会调用播种,但它是空的。问题是混合了两种不同的初始化程序。因此,您必须解决这个问题 - 通过实现 Create... 在内部执行的操作(我们无法调用的调用)或其他方式。

    【讨论】:

    • 您是否知道从哪里获得 EF 4.3.1 的源代码? Microsoft 网站只有 5 或 6 个。
    • 我认为他们已经“开源”只是为了新的更新(即 EF 6)——但源应该是相同的——至少在这一点上。如果你有 DbMigrator 那就差不多了
    • 看起来有一些缺失的部分:using System.Data.Entity.Config; using System.Data.Entity.Internal; using System.Data.Entity.Resources; using System.Data.Entity.Utilities; not found in EF 4.3.1.
    • 试试 DbMigrator...更新 - 我想就是这样 - 你有,我想。如果你有 MigrateDatabase... 初始化器
    • 谢谢,我 99% 在那里。数据库已创建并成功迁移 - 但只有一次。迁移后,当我重新运行应用程序时,它认为数据库未迁移并尝试再次应用更改。这就是我的迁移代码:if (context.Database.Exists()) { if (!context.Database.CompatibleWithModel(throwIfNoMetadata: false)) { var migrator = new DbMigrator(_configuration); migrator.Update(); } }你有什么想法吗?
    【解决方案2】:

    其实应该是:

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

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

    【讨论】:

      【解决方案3】:

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

      这与其他一些数据库初始化程序的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 的数据库初始化程序:

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

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

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

        public Configuration() {
          AutomaticMigrationsEnabled = true
        }
        

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

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

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

      【讨论】:

        【解决方案4】:

        虽然 MigrateDatabaseToLatestVersion 确实会创建数据库(如果它不存在),甚至允许您播种它,但如果您已经有一个基于 CreateDatabaseIfNotExists 和/或的有效解决方案不想通过测试种子数据的存在来使其复杂化,您可以通过从它继承而不是从 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 自己的解决方案。这也应该适用于其他提供程序,但我只使用 SQL Server 进行了测试。

        【讨论】:

          猜你喜欢
          • 2016-04-03
          • 2011-05-05
          • 2013-03-10
          • 2015-03-04
          • 1970-01-01
          • 2015-06-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多