EF 4.3在一个数据库中使用多个DbContexts进行自动迁移

时间:2012-02-02 10:15:53

标签: entity-framework-4 ef-migrations

我正在尝试使用多个代码优先的DbContexts进行EF 4.3迁移。我的应用程序被分成几个插件,这些插件可能有关于其域的自己的DbContext。应用程序应该使用一个单独的sql-database。

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

所以我的问题是:

  • 我如何告诉迁移配置只是为了照顾在相应上下文中定义的表并留下所有其他表单?
  • 在单个数据库中使用自动迁移处理多个DbContexts的正确工作流程是什么?

谢谢!

9 个答案:

答案 0 :(得分:32)

这是你能做的。非常简单。

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

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

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

答案 1 :(得分:6)

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

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

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

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

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

答案 2 :(得分: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
}

由于迁移适用于模型构建器,因此可以完成此任务。

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

答案 3 :(得分:1)

好的,我现在已经在这方面苦苦挣扎了一天,这里有寻求答案的人的解决方案......

我假设大多数读这篇文章的人都在这里,因为他们有一个大的DbContext类,有很多DbSet&lt;&gt;属性,加载需要很长时间。您可能认为自己,gee,这是有意义的,我应该拆分上下文,因为我不会一次使用所有的dbset,而且我只会根据我需要的情况加载“Partial”上下文它。所以你将它们分开,只是发现Code First迁移不支持你的革命思维方式。

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

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

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

这是因为,当前的Migrations模型需要每个数据库使用单个DbContext,并且它必须是镜像匹配。

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

这是我认为的一个重大缺陷。

就我而言,我认为PERFORMANCE比迁移更重要。所以,我最终做的是,在我运行Super上下文并将所有表放到位后,我进入了数据库并手动删除了_MigrationHistory表。

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

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

虽然它对我有用。

答案 4 :(得分:1)

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

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

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

答案 5 :(得分:1)

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

通过这种方式,自动迁移魔法仍然有效。

答案 6 :(得分:0)

我已经开始使用手动迁移,但您无法降级,因为它无法区分__MigrationHistory表中的配置。如果我尝试降级,那么它会将其他配置中的迁移视为自动迁移,因为我不允许数据丢失,所以它会失败。我们只会使用它进行升级,因此它可以用于我们的目的。

虽然看起来确实有点夸张,但我确信如果DbContexts之间没有重叠,那就不难支持它。

答案 7 :(得分:0)

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

似乎有很多人与我分享我的看法,即重复的DbContext与实体超集的引用是一种虚假的非企业友好的方式来处理事情。对于基于模块(Prism或类似)的解决方案,重复的DbContexts失败了。

答案 8 :(得分:0)

我希望人们知道下面的答案对我有用,但有一点需要注意:不要使用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之后两次在YourProjects.Models命名空间下定义了dbcontext(一次在迁移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行(对于我的第二个上下文),最后,现在一切似乎都运行良好。