将 DbContext 拆分为具有重叠 DbSet 的多个上下文

Posted

技术标签:

【中文标题】将 DbContext 拆分为具有重叠 DbSet 的多个上下文【英文标题】:Splitting DbContext into multiple contexts with overlapping DbSets 【发布时间】:2015-09-14 05:22:39 【问题描述】:

我有一个 DbContext,目前包含 +80 个实体,只完成了 4 个主要模块,但还有 3 个要完成,而且它们相当大,因此最多可以轻松达到 150 个。我认为现在是划分上下文的最佳时机。每个模块都使用它自己的实体,并且会获得它自己的上下文,但是所有模块都使用一组实体,所以这里有一个问题:

我是否应该拥有一个包含所有重叠实体的 MainContext,然后:

FK 依赖项会发生什么情况? 嵌套using (var db = new context) 会有多少性能问题,因为我需要从每个模块访问主上下文。

我是否应该将重叠的实体放在所有上下文中,然后

映射会发生什么,不是每个上下文都尝试映射它自己的实体并得到错误吗? 我应该排除重叠上下文在除一个上下文之外的所有上下文上的映射吗?

我应该留在一个上下文中吗?

还有其他建议吗?

【问题讨论】:

不幸的是,每个实体及其所有依赖项(即它的关联)最多只能由一个上下文跟踪。 那么第一个选项是我唯一的解决方案,但是嵌套使用呢? 【参考方案1】:

如果您需要使用跨越多个 DbContext 的事务,您将遇到问题。无论所有 DbContext 是否连接到同一个数据库,它都会被提升为分布式事务。这会使事情变得非常缓慢。

您还将失去工作单元的好处,因为 DbContext 将独立跟踪它们的模型。

您仍然可以分离模型并复制共享模型。这不会导致各种 DbContext 破坏关系或陷入僵局,不会超过两个人同时运行您的软件的两个副本。

但是,为了使事情易于管理,您可以保留在一个 DbContext 中,但隐藏每个模块中不需要的模型。

采取以下DbContext-

public class MyContext : DbContext

    public DbSet<Person> People  get; set; 
    public DbSet<Vehicle> Cars  get; set; 
    public DbSet<Trip> Trips  get; set; 
    public DbSet<Company> Employers  get; set; 
    public DbSet<Employee> Employees  get; set; 

如果你想制作一个驾驶模块,你可能只使用 People、Cars 和 Trips。如果您想要一个工资单模块,您可能只使用 Company、Employee 和 People。所以你需要以下接口:

public interface IDrivingContext

    DbSet<Person> People  get; 
    DbSet<Vehicle> Cars  get; 
    DbSet<Trip> Trips  get; 


public interface IPayrollContext

    DbSet<Person> People  get; 
    DbSet<Company> Employers  get; 
    DbSet<Employee> Employees  get; 

然后你改变你的上下文来实现这两个接口:

public class MyContext : DbContext, IDrivingContext, IPayrollContext

    public DbSet<Person> People  get; set; 
    public DbSet<Vehicle> Cars  get; set; 
    public DbSet<Trip> Trips  get; set; 
    public DbSet<Company> Employers  get; set; 
    public DbSet<Employee>  get; set; 

当您使用DbContext 时,只需将变量键入IDrivingContextIPayrollContext,具体取决于您在哪个模块中编码:

using (IDrivingContext db = new MyDbContext())

     // ...


using (IPayrollContext db = new MyDbContext())

    // ...

【讨论】:

这看起来很有希望,我会试一试并在几个小时内回复你:) 我必须实现 IDisposable 以便我可以在 using 语句中使用它们,但 db.SaveChanges() 不起作用。我应该实现其他东西吗? 如果不同的上下文使用完全相同相同的连接字符串,则不会激活DTS。 您必须将SaveChanges() 添加到接口才能调用它。或者将变量转换为DbContext 那很好。我只是想知道..如果我想从这些接口数据库实例创建通用存储库怎么办.. 那我将如何获得 db.Set() 方法??【参考方案2】:

我认为拆分上下文是一个更好的主意。它使您有可能拥有更轻松的上下文和更轻松的上下文构建时间。此外,如果您对模块进行更改,则它不会影响整个数据库,例如您可以仅部署应用程序的一部分。在不同的上下文中保持对象之间的关系是困难的。您可以在业务层处理此问题并将 Id 存储在数据库中。如果你不能以某种方式分离模型,那么我想你需要把它们放在一起。业务层是处理业务逻辑的地方,在那里你可以停止删除仍然链接的实体。然后,数据库将处理数据库逻辑,例如:类型、大小、身份……我自己正在尝试拆分上下文。从这里开始:EF CORE: is it possible to support two models in one database? 但是我的要复杂得多,并且也有重叠的实体,比如用户都是(创建者、修改者......)

【讨论】:

以上是关于将 DbContext 拆分为具有重叠 DbSet 的多个上下文的主要内容,如果未能解决你的问题,请参考以下文章

DbSet.Attach(实体)与DbContext.Entry(实体).State = EntityState.Modified 区别

在构造函数中存储DbSet而不是调用DbContext.Set 适合各种用途

我可以克隆一个 IQueryable 以在另一个 DbContext 的 DbSet 上运行吗?

动态获取 DbContext 中 Dbset 的行数

DbContext运行时动态附加上一个dbset

EF查询 linq写法 DbContext.DbSet<TEntity>