【问题标题】:EF 4.3 Auto-Migrations with multiple DbContexts in one databaseEF 4.3 自动迁移在一个数据库中具有多个 DbContext
【发布时间】:2012-02-25 00:02:21
【问题描述】:

我正在尝试将 EF 4.3 迁移与多个代码优先 DbContext 一起使用。我的应用程序被分成几个插件,它们可能有自己的 DbContext 关于他们的域。应用程序应该使用一个单一的 sql 数据库。

当我尝试自动迁移空数据库中的上下文时,这仅对第一个上下文成功。每个其他上下文都需要将 AutomaticMigrationDataLossAllowed-Property 设置为 true,然后尝试删除前一个的表。

所以我的问题是:

  • 我如何告诉迁移配置只是照顾在其相应上下文中定义的表,而让所有其他表不理会?
  • 在单个数据库中通过自动迁移处理多个 DbContext 的正确工作流程是什么?

谢谢!

【问题讨论】:

  • 这是一个非常有趣的问题。我想知道多上下文支持是否是迁移用例的一部分。
  • 我非常怀疑多上下文是否可以与自动迁移一起使用,它旨在更新数据库以无论如何看起来就像上下文一样。您可能会更幸运地使用手动迁移开发插件,针对单独的数据库生成迁移,然后将它们全部应用到同一个数据库。
  • 与此同时,我查看了 EF 4.3 程序集,我也怀疑迁移框架能否应对多种情况。但是我想不出技术上的原因。使用 EDM 模型,您可以将其与数据库进行比较,找到创建或更改的现有表表,并通过手动迁移给用户离开删除场景。

标签: entity-framework-4 entity-framework-migrations


【解决方案1】:

这是你可以做的。很简单。

您可以为每个上下文创建配置类。 例如

internal sealed class Configuration1 : DbMigrationsConfiguration<Context1>{
   public Configuration1 (){
        AutomaticMigrationsEnabled = false;
        MigrationsNamespace = "YourProject.Models.ContextNamespace1";
   }
}

internal sealed class Configuration2 : DbMigrationsConfiguration<Context2>{
   public Configuration2 (){
        AutomaticMigrationsEnabled = false;
        MigrationsNamespace = "YourProject.Models.ContextNamespace2";
   }
}

现在您添加迁移。您不需要启用迁移,因为您已经使用上述 2 类。

Add-Migration -configuration Configuration1 Context1Init

这将为 context1 创建迁移脚本。您可以对其他上下文再次重复此操作。

Add-Migration -configuration Configuration2 Context2Init

更新你的数据库

Update-Database -configuration Configuration1
Update-Database -configuration Configuration2

这可以按任何顺序完成。除非您需要确保按顺序调用每个配置。

【讨论】:

  • 完美,它说“请指定要使用的那个”,我不知道该怎么做。谢谢!
  • 当我尝试使用 Context1 时,我得到“支持上下文的模型已更改”
  • 天哪,这应该被标记为答案!!!太感谢了!以防万一我让它在 MVC 5 和 EF 6 上使用这个解决方案
  • @GLM,此解决方案需要 EF 6.0 或更高版本。没有它,您将遇到该错误和许多其他错误。
  • @Brice 这也适用于 EF 4.3,它不是 6.0 的具体细节。
【解决方案2】:

Code First 迁移假设每个数据库只有一个迁移配置(每个配置一个上下文)。

我能想到两种可能的解决方案:

  1. 创建一个包含每个上下文的所有实体的聚合上下文,并从迁移配置类中引用这个“超级”上下文。这样,所有表都将在用户的数据库中创建,但数据只会在他们为其安装插件的表中。

  2. 为每个上下文使用单独的数据库。如果您在上下文之间共享实体,请添加自定义迁移并将 CreateTable(...) 调用替换为 Sql("CREATE VIEW ...") 调用以从实体的“原始”数据库中获取数据。

我会尝试 #1,因为它将所有内容都保存在一个数据库中。您可以在解决方案中创建一个单独的项目来包含您的迁移和这个“超级”上下文。只需添加项目,引用所有插件的项目,创建一个包含所有实体的上下文,然后在这个新项目上调用 Enable-Migrations。之后事情应该会按预期进行。

【讨论】:

  • 我接受这个答案,不管它不是我问题的真正答案。我认为将迁移设计到每个数据库只有一个上下文是错误的。一方面,每个具有未在上下文中使用的表的遗留数据库都会产生问题。另一方面(即使是预编译的视图),具有大约 100 个不同实体的上下文的应用程序域需要很长时间才能启动。因此,将其拆分为更小的上下文是迄今为止唯一的解决方案。
  • 我原来的答案已经有点过时了。 EF 6.0 消除了每个数据库一个 Code First 上下文的限制。在 6.0 及更高版本中,事情应该“正常工作”。
【解决方案3】:

我有一个使用迁移的具有多个上下文的工作站点。但是,您确实需要为每个上下文使用一个单独的数据库,并且它都是由项目的 Migrations 命名空间中的 *Configuration 类驱动的,例如,CompanyDbContext 使用 CompanyConfiguration 指向 Company.sdf。 update-database -configurationtypename CompanyConfiguration。另一个 LogDbContext 使用 LogConfiguration 等指向 Log.sdf。

鉴于此工作,您是否尝试过创建 2 个指向同一个数据库的上下文并告诉模型构建器忽略其他上下文的表列表?

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Ignore<OtherContextsClass>();
    // more of these
}

由于迁移与模型构建器一起工作,这可能会完成这项工作。

糟糕的替代方法是避免使用自动迁移,每次都生成迁移,然后手动筛选并删除不需要的语句,然后运行它们,尽管没有什么能阻止您创建一个查看上下文和生成的语句的简单工具并为您进行迁移修复。

【讨论】:

  • 谢谢你的回答。但不幸的是我无法拆分数据库。但是你给了我一个很好的提示,让我使用自动迁移的输出作为手动迁移的基础。
【解决方案4】:

好的,我已经为此苦苦挣扎了一天,这里是寻求答案的人的解决方案...

我假设大多数阅读这篇文章的人都在这里,因为他们有一个带有很多 DbSet 属性的大型 DbContext 类,并且加载需要很长时间。您可能会想,哎呀,这很有意义,我应该拆分上下文,因为我不会一次使用所有 dbset,我只会根据需要的情况加载“部分”上下文它。因此,您将它们分开,却发现 Code First 迁移不支持您的革命性思维方式。

所以您的第一步一定是拆分上下文,然后为每个新上下文添加了 MigrationConfiguration 类,添加了与新上下文类命名完全相同的连接字符串。

然后您尝试通过执行 Add-Migration Context1 然后执行 Update-Database -Verbose 来逐个运行新拆分的上下文...

一切似乎都运行良好,但随后您注意到每次后续迁移都删除了上一次迁移中的所有表,并且只保留了上次迁移中的表。

这是因为,当前的迁移模型要求每个数据库有一个 DbContext,并且它必须是镜像匹配。

我也尝试过,并且有人在这里建议这样做,是创建一个 SuperContext,其中包含所有 Db 集。创建单个迁移配置类并在其中运行。将部分上下文类保留在原位,并尝试实例化并使用它们。 EF 抱怨 Backing 模型发生了变化。同样,这是因为 EF 会将您的部分 dbcontext 与您的 Super Context 迁移留下的 All-Sets 上下文签名进行比较。

在我看来,这是一个重大缺陷。

就我而言,我认为性能比迁移更重要。所以,我最终做的是,在我在 Super 上下文中运行并准备好所有表之后,我进入数据库并手动删除了 _MigrationHistory 表。

现在,我可以在没有 EF 抱怨的情况下实例化和使用我的部分上下文。它没有找到 MigrationHistory 表,只是继续前进,让我拥有数据库的“部分”视图。

当然要权衡的是,对模型的任何更改都必须手动传播到数据库,所以要小心。

它对我有用。

【讨论】:

    【解决方案5】:

    正如上面 Brice 所说,最实用的解决方案是每个应用程序/数据库有 1 个超级 DbContext。

    整个应用程序只能使用 1 个 DbContext 似乎是一个关键的技术和方法劣势,因为它会影响模块化等。此外,如果您使用 WCF 数据服务,则每个应用程序只能使用 1 个数据服务,因为数据服务只能映射到 1 个 DbContext。所以这大大改变了架构。

    从好的方面来说,一个小优势是所有与数据库相关的迁移代码都是集中的。

    【讨论】:

      【解决方案6】:

      我刚刚遇到了这个问题,并意识到我将它们分成不同的上下文的原因纯粹是为了将相关模型分组到可管理的块中,而不是出于任何其他技术原因。相反,我已将上下文声明为分部类,现在具有不同模型的不同代码文件可以将 DbSet 添加到 DbContext。

      这样,自动迁移魔法仍然有效。

      【讨论】:

        【解决方案7】:

        我已经让它与手动迁移一起工作,但你不能降级,因为它不能区分 __MigrationHistory 表中的配置。如果我尝试降级,那么它会将来自其他配置的迁移视为自动迁移,并且由于我不允许数据丢失,它会失败。不过,我们只会使用它来升级,因此它适用于我们的目的。

        虽然它看起来确实有点 omision,但如果 DbContext 之间没有重叠,我相信支持它并不难。

        【讨论】:

          【解决方案8】:

          当然,解决方案应该是 EntityFramework 团队进行修改,以更改 API 以支持将 _MigrationHistory 表直接修改为您选择的表名,例如 _MigrationHistory_Context1 ,以便它可以处理独立 DbContext 实体的修改。这样一来,它们都被分开处理,并由开发人员确保实体的名称不会发生冲突。

          似乎有很多人同意我的观点,即引用实体超集的重复 DbContext 是一种虚假的非企业友好的处理方式。对于基于模块化(Prism 或类似)的解决方案,重复的 DbContext 会失败。

          【讨论】:

            【解决方案9】:

            我想让人们知道,下面的答案对我有用,但有一个警告:不要使用 MigrationsNamespace 行。

            internal sealed class Configuration1 : DbMigrationsConfiguration<Context1>{
                   public Configuration1 (){
                    AutomaticMigrationsEnabled = false;
                    MigrationsNamespace = "YourProject.Models.ContextNamespace1";
               }
             }
            
            internal sealed class Configuration2 : DbMigrationsConfiguration<Context2>{
               public Configuration2 (){
                    AutomaticMigrationsEnabled = false;
                    MigrationsNamespace = "YourProject.Models.ContextNamespace2";
               }
            }
            

            但是,我已经建立了 2 个数据库并定义了它们自己的上下文,因此我发现自己收到一条错误消息,提示“YourProject.Models 命名空间已经定义了 ContextNamespace1”。这是因为 "MigrationsNamespace = "YourProject.Models.ContextNamespace2";"在我尝试 Init 之后,导致 dbcontext 在 YourProjects.Models 命名空间下定义了两次(一次在迁移 Context1Init 文件中,一次在我之前定义的位置)。

            所以,我发现当时我必须做的是按照此处的说明从头开始我的数据库和迁移(幸好我没有需要保留的数据): http://pawel.sawicz.eu/entity-framework-reseting-migrations/

            然后我将代码更改为不包含 MigrationsNamespace 行。

            internal sealed class Configuration1 : DbMigrationsConfiguration<Context1>{
                   public Configuration1 (){
                    AutomaticMigrationsEnabled = false;
               }
             }
            
            internal sealed class Configuration2 : DbMigrationsConfiguration<Context2>{
               public Configuration2 (){
                    AutomaticMigrationsEnabled = false;
               }
            }
            

            然后我再次运行 Add-Migration -configuration Configuration1 Context1Init 命令和 Update-Database -configuration Configuration1 行(对于我的第二个上下文也是如此),最后,现在一切似乎都很好。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-08-03
              • 1970-01-01
              • 2014-10-08
              • 1970-01-01
              • 1970-01-01
              • 2018-11-20
              相关资源
              最近更新 更多