18

我有一个自定义的 DatabaseInitialiser,它在下面

/// <summary>
/// Implements the IDatabaseInitializer to provide a custom database initialisation for the context.
/// </summary>
/// <typeparam name="TContext">TContext is the DbContext</typeparam>
public class ParikshaDataBaseInitializer<TContext> : IDatabaseInitializer<TContext> where TContext : DbContext
{
    /// <summary>
    /// The method to Initialise the database.
    /// Takes care of the database cannot be dropped since it is in use problem while dropping and recreating the database.
    /// </summary>
    /// <param name="context">The DbContext on which to run the initialiser</param>
    public void InitializeDatabase(TContext context)
    {
        var exists = context.Database.Exists();

        try
        {
            if (exists && context.Database.CompatibleWithModel(true))
            {
                // everything is good , we are done
                return;
            }

            if (!exists)
            {
                context.Database.Create();
            }
        }
        catch (Exception)
        {
            //Something is wrong , either we could not locate the metadata or the model is not compatible.
            if (exists)
            {
                context.Database.ExecuteSqlCommand("ALTER DATABASE Pariksha SET SINGLE_USER WITH ROLLBACK IMMEDIATE");
                context.Database.ExecuteSqlCommand("USE Master DROP DATABASE Pariksha");
                context.SaveChanges();
            }

            context.Database.Create();
        }
    } 
}

关于上述代码的一些东西不仅仅是hacky(请随时提供帮助)

然后我添加了迁移并使迁移脚本也能正常工作。

    internal sealed class Configuration : DbMigrationsConfiguration<ParikshaContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = false;
            ContextKey = "EFRepository.Context.ParikshaContext";
        }

        protected override void Seed(ParikshaContext context)
        {
        }
    }

迁移按预期工作。

现在,问题是在我的应用程序启动中我应该怎么做?像这样的东西

 var config = new Configuration();
 var migrator = new DbMigrator(config);
 migrator.Update();

并且一些论坛也在构造函数中提出了这一点,这似乎有点奇怪,因为我不想在每次使用上下文时检查数据库和模式是否正确。那么,这种技术的可能用途是什么,或者我认为建议的上下文是错误的?

public ParikshaContext() : base("Pariksha")
        {           
          Database.SetInitializer(new ParikshaDataBaseInitializer<ParikshaContext>());
        }

总结一下,

  1. 可用的不同技术的正确用例是什么?

  2. 什么是理想的策略,以便迁移在所有条件下以及当我们将数据库从一个环境移动到另一个环境时工作?

4

2 回答 2

12

这是我尝试Db InitializerMigration初始化程序和默认程序Db Create与播种结合起来的尝试。(注意:这并不理想,更像是一个简单的练习,但可以解决您在此处提出的问题,大多数情况下都有效 - 只需检查我所做的所有更新)。

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

至于whyand how- 为了完全理解,我建议您也参考EF 源代码(这是新版本,但在许多方面相似)

1)

a) Db 初始化程序通常只被调用一次(每个连接) - 当你第一次尝试访问你的“模型”时(第一次查询或类似的)。在初始化程序中放置一个断点以进行检查。

因此,将它放在构造函数中是完全安全的(尽管我更喜欢在某个地方启动它,也可以配置)。它仅在需要初始化(并且last one set已使用)时才被调用,您不应手动调用它。

无论如何,要强制初始化器,您可以执行this.Database.Initialize(force: true);

对于切换连接时,请参阅我关于问题的帖子
Code first custom connection string and migrations without using IDbContextFactory

b)如果您创建自己的IDatabaseInitializer并且仍然希望迁移工作side by side

您不应该只DbMigrator从外部调用- 因为您的自定义初始化程序将错过整个“数据库创建”(例如,如果您希望播种或其他内容 - 请查看我上面的示例)。

这两件事实际上都是“初始化程序” - 所以你需要将它们集成到一个中,这chain在某种程度上就是这样。请记住这order of execution很重要(请参阅上面的问题示例)-您应该检查“空条件”,然后调用DbMigrator,然后进行自己的初始化。我使用了一个初始化器作为基类,并合并了另一个。

如果你只是想seed- 你可以使用迁移配置,这是最简单的,如果合理的话。

2)

非常“开放式”,没有单一的答案。通常它可以工作,但问题是expexted ...

  • 迁移是三件事(如我所见) - 您的代码模型/实体、数据库/表和__MigrationHistoryDb 中的系统表。所有 3 需要留下来in sync。如果您“不同步”,您可以删除迁移表,重新创建迁移(使用保留现有数据库的标志),然后像以前一样继续 - 即有针对何时使用实时数据的解决方案。为此,请参阅如何在 EF 4.3 迁移中忽略表/类

  • 移动数据库时,您需要删除/创建 Db 的权限,

  • 确保您的连接正确(更改配置 - 并与您的 DbContext 名称或 ctor 同步),

  • 保持简单,不要做花哨的事情或从代码切换连接(可能但有问题)等,

  • 不要mix database / code版本 - 即一个代码实体版本 - 一个数据库。如果您想与不同的代码版本(例如暂存、生产)共享同一个 Db - 不要(EF6 中将提供多租户解决方案 - 例如这个),

  • 如果您需要手动应用数据库 - 生成script通过Update-Database- 并应用它,请不要手动执行操作,否则您会弄错(迁移历史表) - 请参阅这个

……仅举几例。它非常稳定且可用 IMO - 但如果您遵守规则 - 并知道限制是什么。


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)
    {
        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(false))
        if (migrator.GetPendingMigrations().Any())
            migrator.Update();

        // move on with the 'CreateDatabaseIfNotExists' for the 'Seed'
        base.InitializeDatabase(context);
        if (doseed)
        {
            Seed(context);
            context.SaveChanges();
        }
    }
    protected override void Seed(TContext context)
    {
    }
}
于 2013-04-21T23:47:31.203 回答
4

总结一下,

1) what is the correct use-case for the different techniques available ?

2) what would be the ideal strategy so that the migrations work in all conditions 
and when we move databases from one environment to another ?

退后一步,问问自己何时希望 EF 进行初始化和迁移。在我的(企业)组织中,我们对如何迁移代码、数据和 DDL 有非常严格的规定,因此这些功能对我们没有任何价值。即使我们可以使用它们,我也会非常谨慎,直到我在不同环境中对该工具有相当多的经验。

那么,为什么要使用它呢?您正在开始一个新的绿地项目并使用代码优先。现在,这就是它有用的地方。您可以编写代码、编译、运行并让 EF 担心新的或缺失的字段、关系和表。稍后,您可以在您的开发服务器中对其进行形式化,然后永远删除初始化器和迁移器。

至于怎么设置?听起来您找到了一种可行的方法。我同意这似乎有点 hacky,但代码类似于我的初始设置。我正在使用 MVC.NET,所以我在 Global.asax.cs 中使用了以下内容:

 Database.SetInitializer(new DomainInitializer());

底线是我认为你的(2)是错误的问题。应该是“我真的要这样做吗?”。事实证明你可能会这样做,但我会首先考虑其他更传统的选择,特别是如果你在一家超过 10 人的公司中。

于 2013-04-22T23:29:42.497 回答