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

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

我已经学习如何使用 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());

最佳答案

我想你已经差不多了 - 你可以查找 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());

编辑: 根据评论 - 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... 在内部执行的操作(我们无法调用的调用)或其他方式。

关于c# - 如何创建初始化程序来创建和迁移 mysql 数据库?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15796115/

相关文章:

c# - 对象引用未设置为组合框的对象实例

c# - 无窗口wpf应用程序示例?

Mysql删除内连接不起作用?

mysql - "union all"的多重选择不起作用

struts2 - Struts 1 到 Struts 2 的迁移策略

c# - 如何使静态类使用 C++ 引用字符串变量?

java - 数据库中添加NULL

mysql - Laravel 迁移分隔符

git - SVN -> 具有结构变化的 Git 迁移

c# - 如何跨多个项目共享cshtml页面?