【问题标题】:Why inject the entire DbContext instead of just the necessary DbSets?为什么要注入整个 DbContext 而不是只注入必要的 DbSet?
【发布时间】:2021-08-09 09:10:15
【问题描述】:

我看过很多项目是否将整个DbContext注入到一个类中:

public class MyClass
{
    public MyClass(MyDbContext context)
    {
        ...
    }
    
    public void DoStuff()
    {
         dbContext.MyTable.Select(x => x...)
    }
}

为什么不这样做:

public class MyClass
{
    public MyClass(DbSet<MyType> myTypes)
    {
        ...
    }
    
    public void DoStuff()
    {
         myTypes.Select(x => x...)
    }
}

我发现 #2 更清楚地说明了对象需要操作哪些数据。它使单元测试更加清晰,因为从构造函数中可以明显看出依赖关系。否则,您必须检查该方法的实现以找出它确切需要哪些数据,以便正确设置测试。

那么为什么 #1 是常态?

(P.S. 抱歉标签不好,但似乎标签系统已损坏)

【问题讨论】:

  • 因为你需要 DbContext 来做任何有用的事情,比如保存对数据库的更改。此外,因为将 20 DbSet&lt;T&gt; 注入 DI 比仅注入 DbContext 更痛苦。可能还有很多其他的事情
  • @CamiloTerevinto 为什么 SaveChanges 不能作为扩展方法来弥补它不在 DbSet 上?对于注册,可以只注册所有可在上下文中分配给 DbSet 的公共属性。所以不需要手动添加Di中的所有DbSet。
  • 好吧,使用扩展方法只会隐藏对 DbContext 的依赖,让测试变得更加困难
  • DbContext lifetime。注意重要部分。
  • 我不同意这里的大多数观点,并且更喜欢看到注入单个存储库(EF 术语中的 DbSet)而不是整个数据库上下文。例如。 1. 业务层不知道事务何时完成——它只知道它的工作何时完成。 2. 谁编写了一个更新 20 个存储库的类?

标签: c# entity-framework dependency-injection anti-patterns


【解决方案1】:

通常开发人员编写单元测试来测试业务用例,数据访问层不应包含任何逻辑,这就是为什么人们使用Repository Pattern 来隐藏数据访问层的实现。

否则,您必须检查方法的实现以 找出它确切需要的数据,以便您可以设置测试 正确。

MyDbContextDbSet&lt;MyType&gt; 与数据访问有关,当您尝试为DoStuff 方法实现单元测试时,您不应该考虑编写测试查询的实现,您唯一需要的是模拟@987654329 @ 或 DbSet&lt;MyType&gt;。此外,当使用具体类作为依赖项时,您违反了Dependency Inversion Principle,这就是为什么您必须查看DoStuff 方法的实现以找出哪些数据以及数据来自何处。

【讨论】:

  • 可能是一个接口... IDbSet 接口什么的。而且我认为很多人反对将存储库模式与 EF 一起使用:thereformedprogrammer.net/…
  • IDbSet 仍然是一种存储库,它隐藏了实际DbSet&lt;T&gt; 的实现,整个想法是使用抽象而不是使用具体实现。
  • DbContext 一个存储库。 DbSet&lt;T&gt; 一个工作单元。每当有人提出这种无用的抽象时,我都会感到畏缩。除了极少数情况下,公司可能实际上决定在一个项目上花费数千美元只是为了摆脱 EF,基本上没有任何好处。我已经看到这种隐藏 EF 的情况主要是开发人员无法意识到如何正确测试 EF 并使遵循架构变得异常困难
  • DbSet&lt;T&gt; is a Unit of Work,当我添加到DbSet&lt;MyType&gt;.Add(myType) 时,所有数据都被保留? DbSets 是存储库,UoF 在调用 SaveChanges 方法时应用。 DbContext is a combination of the Unit Of Work and Repository patterns。但是,您可以阅读我关于支付嘲笑DbSet 的文章,这是一种泄漏的抽象dev.to/moesmp/…
【解决方案2】:

因为“保存”在 dbcontext,而不是子集合上。

SaveChangesAsync”作为最重要的一项。但下面列出了其他人。

        int saveChangesAsyncValue = await this.myDbContext.SaveChangesAsync(token);

框架:

https://docs.microsoft.com/en-us/dotnet/api/system.data.entity.dbcontext.savechangesasync?view=entity-framework-6.2.0

核心:

https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbcontext.savechangesasync?view=efcore-5.0

其他(核心):

https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbcontext.savechanges?view=efcore-5.0

您应该真正专注于对您的业务逻辑进行单元测试。巧妙地,这也意味着。

  1. 停止将(任何)业务逻辑放入数据层。 (以及它的亲亲表亲,停止将业务逻辑放在您的表示层中)。

您应该将数据层的(模拟)注入到您的业务逻辑类中。

话虽如此,EF InMemoryDatabase 有点模糊了界限。

但这并不能避免必须提供“myDbContext”。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-24
    • 1970-01-01
    • 2020-03-03
    • 2021-02-24
    • 1970-01-01
    • 2010-11-01
    相关资源
    最近更新 更多