entity framework:一个数据库,多个DbContexts。 这是一个坏主意吗?

我迄今为止的印象是,DbContext是为了代表你的数据库,因此,如果你的应用程序使用一个数据库,你只需要一个DbContext。 但是,有些同事想把function区分成单独的DbContext类。 我相信这是来自一个好地方 – 希望保持代码更清洁 – 但似乎是不稳定的。 我的直觉告诉我这是一个坏主意,但不幸的是我的直觉并不是devise决定的充分条件。

所以我正在寻找A)具体的例子,为什么这可能是一个坏主意,或者B)保证这一切都会运行得很好。

你可以有多个上下文单个数据库。 例如,如果您的数据库包含多个数据库模式,并且您想将它们分别处理为独立的自包含区域,那么这会非常有用。

问题是,当你想先使用代码来创build你的数据库 – 只有你的应用程序中的单个上下文可以做到这一点。 这个技巧通常是一个额外的上下文,它包含所有只用于数据库创build的实体。 只包含实体子集的实际应用程序上下文必须将数据库初始化程序设置为null。

当使用多个上下文types时,您会看到其他问题 – 例如共享实体types及其从一个上下文传递到另一个上下文等。一般情况下,它可以使您的devise更清晰并分离不同的function区域,但它有成本增加了复杂性。

这个线程刚刚在StackOverflow上冒出来,所以我想提供另一个“B”保证,这将是一切正常“:)

我正在通过DDD有限上下文模式来完成这个工作。 我已经在我的书“编程entity framework:DbContext”中写了一个关于它的内容,它是我的Pluralsight课程中的一个50分钟模块的重点 – > http://pluralsight.com/training/Courses/TableOfContents/efarchitecture

通过设置默认的schmema来区分上下文

在EF6中,您可以有多个上下文,只需在DbContext派生类的OnModelCreating方法(Fluent-APIconfiguration所在的位置)中指定默认数据库模式的名称即可。 这将在EF6中工作:

 public partial class CustomerModel : DbContext { protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.HasDefaultSchema("Customer"); // Fluent API configuration } } 

这个例子将使用“Customer”作为数据库表的前缀(而不是“dbo”)。 更重要的是,它也会以__MigrationHistory表格为前缀,例如Customer.__MigrationHistory 。 所以你可以在一个数据库中有多个__MigrationHistory表,每个上下文一个。 所以你为一个上下文做出的改变不会混淆另一个上下文。

添加迁移时,请在add-migration命令中指定您的configuration类的完全限定名称(从DbMigrationsConfiguration派生)作为参数:

 add-migration NAME_OF_MIGRATION -ConfigurationTypeName FULLY_QUALIFIED_NAME_OF_CONFIGURATION_CLASS 

关于上下文关键字的简短字

根据这篇MSDN文章“ Chapter-Multiple Models Targeting the Same Database ”,即使只有一个MigrationHistory表存在,EF 6也可能会处理这种情况,因为表中有一个ContextKey列来区分迁移。

不过,我更喜欢通过指定上面解释的默认模式来拥有多个MigrationHistory表。

使用单独的迁移文件夹

在这种情况下,您可能还想在项目中使用不同的“迁移”文件夹。 您可以使用MigrationsDirectory属性相应地设置您的DbMigrationsConfiguration派生类:

 internal sealed class ConfigurationA : DbMigrationsConfiguration<ModelA> { public ConfigurationA() { AutomaticMigrationsEnabled = false; MigrationsDirectory = @"Migrations\ModelA"; } } internal sealed class ConfigurationB : DbMigrationsConfiguration<ModelB> { public ConfigurationB() { AutomaticMigrationsEnabled = false; MigrationsDirectory = @"Migrations\ModelB"; } } 

概要

总而言之,您可以说所有东西都干净地分开:项目中的上下文,迁移文件夹和数据库中的表格。

我会select这样的解决scheme,如果有一组实体是一个更大的主题的一部分,但不相关(通过外键)彼此。

如果实体组彼此之间没有任何关系,我会为每个实体创build一个单独的数据库,并且也可以在不同的项目中访问它们,可能每个项目中只有一个上下文。

我会反对这个想法,用真实的经验来支持我的投票。

我被带到一个有五个上下文的大型应用程序中。 最后,我们最终删除了除一个之外的所有上下文 – 恢复到单个上下文。

起初,多种语境的想法似乎是一个好主意。 我们可以将我们的数据访问分离到域中并提供几个干净的轻量级上下文。 听起来像DDD,对吧? 这将简化我们的数据访问。 另一个论据是为了performance我们只能访问我们需要的上下文。

但实际上,随着我们的应用程序的增长,我们的许多表格在不同的环境下都分享了关系。 例如,对上下文1中的表A的查询也需要在上下文2中连接表B.

这给我们留下了一些糟糕的select。 我们可以在不同的上下文中复制表格。 我们试过这个。 这创造了几个映射问题,包括EF约束,要求每个实体具有唯一的名称。 所以我们结束了在不同的上下文中名为Person1和Person2的实体。 有人可能会认为这是我们糟糕的devise,但尽pipe我们尽了最大的努力,这就是我们的应用程序在现实世界中实际上的增长。

我们也尝试查询两个上下文来获取我们需要的数据。 例如,我们的业务逻辑将从上下文1中查询一半所需的内容,而从上下文2查询另一半。这存在一些主要问题。 不是针对单个上下文执行一个查询,而是必须在不同的上下文中执行多个查询。 这有一个真正的性能损失。

最后,好消息是很容易去除多种情况。 上下文旨在成为一个轻量级的对象。 所以我不认为性能是多种情况下的一个好的参数。 在几乎所有的情况下,我认为单一的上下文更简单,更简单,并且可能performance更好,而且不需要执行一系列的解决方法来实现它。

我想到了一种情况,在多种情况下可能是有用的。 可以使用单独的上下文来修复实际上包含多个域的数据库的物理问题。 理想情况下,上下文对于一个域来说是一对一的,这对于一个数据库是一对一的。 换句话说,如果一组表与一个给定的数据库中的其他表没有任何关系,那么它们应该被拉出到一个单独的数据库中。 我意识到这并不总是实际的。 但是,如果一组表格如此不同以至于你觉得把它们分离成一个单独的数据库(但你select不这样做),那么我可以看到使用单独的上下文的情况,但仅仅是因为实际上有两个单独的域。

我对你的想法感兴趣。

简单的例子来实现如下:

  ApplicationDbContext forumDB = new ApplicationDbContext(); MonitorDbContext monitor = new MonitorDbContext(); 

只需在主要上下文中对属性进行范围:(用于创build和维护数据库)注意:只需使用protected:(实体不在此处公开)

 public class ApplicationDbContext : IdentityDbContext<ApplicationUser> { public ApplicationDbContext() : base("QAForum", throwIfV1Schema: false) { } protected DbSet<Diagnostic> Diagnostics { get; set; } public DbSet<Forum> Forums { get; set; } public DbSet<Post> Posts { get; set; } public DbSet<Thread> Threads { get; set; } public static ApplicationDbContext Create() { return new ApplicationDbContext(); } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); } } 

MonitorContext:在这里公开单独的实体

 public class MonitorDbContext: DbContext { public MonitorDbContext() : base("QAForum") { } public DbSet<Diagnostic> Diagnostics { get; set; } // add more here } 

诊断模型:

 public class Diagnostic { [Key] public Guid DiagnosticID { get; set; } public string ApplicationName { get; set; } public DateTime DiagnosticTime { get; set; } public string Data { get; set; } } 

如果您喜欢,您可以将所有实体标记为主ApplicationDbContext中的protected,然后根据需要为每个模式分离创build其他上下文。

他们都使用相同的连接string,但是他们使用单独的连接,所以不要交叉事务,并意识到locking问题。 通常你的devise分离,所以这不应该发生。

提醒:如果组合了多个上下文,请确保您将各种RealContexts.OnModelCreating()所有function粘贴到单个CombinedContext.OnModelCreating()

我只是浪费时间追查为什么我的级联删除关系不被保存,只发现我没有移植modelBuilder.Entity<T>()....WillCascadeOnDelete(); 代码从我的真实环境到我的综合环境。

当我遇到这个devise时,我的直觉告诉了我同样的事情。

我正在一个代码库上有三个dbContexts到一个数据库。 3个dbcontext中的2个依赖于来自1个dbcontext的信息,因为它提供了pipe理数据。 这个devise限制了你如何查询你的数据。 我遇到了这个问题,你不能跨越dbcontexts连接。 相反,你需要做的是查询两个单独的dbcontexts,然后在内存中进行连接,或者遍历两者以得到两者的组合。 问题在于,不是查询特定的结果集,而是将所有logging加载到内存中,然后对内存中的两个结果集进行连接。 它可以真的放慢速度。

我会问这个问题:“ 只因为你可以,对不对?

看到这篇文章我遇到的问题涉及到这个devise。 指定的LINQexpression式包含对与不同上下文关联的查询的引用

在代码中,你可以有多个DBContext和一个数据库。 你只需要在构造函数中指定连接string。

 public class MovieDBContext : DbContext { public MovieDBContext() : base("DefaultConnection") { } public DbSet<Movie> Movies { get; set; } } 

另一点“智慧”。 我有一个数据库,面向互联网和一个内部的应用程序。 我有每个面孔的背景。 这有助于我保持纪律,安全的隔离。

受[@JulieLerman的DDD MSDN Mag Article 2013]启发[1]

  public class ShippingContext : BaseContext<ShippingContext> { public DbSet<Shipment> Shipments { get; set; } public DbSet<Shipper> Shippers { get; set; } public DbSet<OrderShippingDetail> Order { get; set; } //Orders table public DbSet<ItemToBeShipped> ItemsToBeShipped { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) {   modelBuilder.Ignore<LineItem>();   modelBuilder.Ignore<Order>();   modelBuilder.Configurations.Add(new ShippingAddressMap()); } } 

 public class BaseContext<TContext> DbContext where TContext : DbContext { static BaseContext() { Database.SetInitializer<TContext>(null); } protected BaseContext() : base("DPSalesDatabase") {} } 

“如果您正在进行新的开发,并且希望让Code First根据您的类创build或迁移您的数据库,则需要使用DbContext创build一个”超级模型“,其中包含所需的所有类和关系build立一个代表数据库的完整模型,但是这个上下文不能从BaseContextinheritance。 JL