【问题标题】:What is the correct use of IDatabaseInitializer in EF?EF 中 IDatabaseInitializer 的正确用法是什么?
【发布时间】:2013-04-06 14:35:56
【问题描述】:

我有一个自定义的 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. 当我们将数据库从一个环境迁移到另一个环境时,什么是理想的策略才能使迁移在所有条件下都有效?

【问题讨论】:

    标签: c# entity-framework ef-code-first entity-framework-migrations entity-framework-6


    【解决方案1】:

    这是我对 Db Initializer 的尝试,它结合了 Migration 初始化器和默认的 Db Create 一个与播种。 (注意:这并不理想,更像是一个简单的练习,但可以解决您在此处提出的问题,主要是可行的 - 只需检查我所做的所有更新)。

    How to create initializer to create and migrate mysql database?

    至于whyhow - 要完全理解我建议您也参考EF source code(这是新版本,但在许多方面相似)

    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 ...

    • 迁移是 3 件事(在我看来) - 您的代码模型/实体、您的数据库/表和数据库中的 __MigrationHistory 系统表。所有 3 人都需要留下in sync。如果您“不同步”,您可以删除迁移表,重新创建迁移(带有保留现有数据库的标志),然后像以前一样继续 - 即有实时数据时的解决方案。为此请参阅How to ignore a table/class in EF 4.3 migrations

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

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

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

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

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

    ...仅举几例。它非常稳定且可用 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)
        {
        }
    }
    

    【讨论】:

    • 您在大多数示例中所走的路径与我要走的路径太相似,我根据您的信息更改了一些部分,如果您知道解决方案将对此提出问题很高兴听到它。这是我的:stackoverflow.com/questions/17533721/…
    • @LastBye thx - 我会试着看看,只是有点忙(最近不太活跃),需要几天时间
    • thx 也许我将它从 SO 中删除,并在 SkyD 中发布了一个完整的解决方案。如果我这样做了,我会在这里以厘米的形式分享链接。
    • sdrv.ms/14atOs5 这是我的文件。请看一下,让我知道您的提示,并在 gm-ail 中添加了您。
    • 请看my question,我想你可以帮助我。
    【解决方案2】:

    总结一下,

    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 人的公司中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-06-02
      • 1970-01-01
      • 2015-06-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-09
      • 2011-04-15
      相关资源
      最近更新 更多