【问题标题】:Is it bad to have a captive dependency on DbContext instance?对 DbContext 实例具有强制依赖关系是不是很糟糕?
【发布时间】:2019-01-21 17:30:34
【问题描述】:

也许这个问题有一些解释,但找不到最好的解决方案:

我正在阅读 Mark Seemann 的这篇 blog post 关于强制依赖项的内容,据我所知,在帖子末尾,他得出结论永远不要使用或至少尝试避免强制依赖项,否则会有麻烦( 到目前为止还可以)。这是 Autofac 文档中的另一个 post

他们建议仅根据目的当你知道自己在做什么!)使用强制依赖。这让我想到了我网站上的情况。我有 10 个服务,它们都依赖DbContext 进行数据库操作。我认为他们可以很容易地注册为InstancePerLifetimeScope,如果我解决DbContext 的问题,而不是将其永久保存在我的服务的内存中。 (就我而言,我使用的是 Autofac)。所以,我认为一个好的起点是根据生命周期实例创建所有这些,并将DbContext 作为每个请求的实例。然后在我的服务中,我会使用类似的东西:

public class MyService
{
    private readonly IDbContext _dbContext = DependencyResolver.Current.GetService<IDbContext>();

    private MyModel GetMyModel() => Mapper.Map<MyModel>(_dbContext.MyTable.FirstOrDefault());
}

然后在我的启动课上我有:

builder.RegisterType<ApplicationDbContext>().As<IDbContext>().InstancePerRequest();
builder.RegisterType<MyService>().As<IMyService>().InstancePerLifetimeScope();

此模式是否正常工作,我的意思是不要将dbContext 永远附加到任何服务,因此它将在请求结束时被处理,如果它有效,则此行是否存在任何性能问题:

 private readonly IDbContext _dbContext = DependencyResolver.Current.GetService<IDbContext>();

与构造函数注入相比(从 dbContext 到数据库的调用很多,所以每次我想使用它时我都害怕得到IDbContext,因为它可能会消耗资源)?

我希望 dbContext 成为每个请求的实例而不是每个依赖项的实例的原因是我在 dbContext 对象之上实现了工作单元模式。

我的控制器中的普通方法如下所示:

public ActionResult DoSth()
{
     using(var unitOfWork = UnitOfWorkManager.NewUnitOfWork())
     { 
          //do stuff
          try
          {
              unitOfWork.Commit();
              return View();
          }
          catch(Exception e)
          {
              unitOfWork.RollBack();
              LoggerService.Log(e);
              return View();
          }
     }
}

如果这工作正常,那么还有另一个我担心的问题。因此,如果我可以将我的服务作为每个生命周期的实例(DbContext 除外),那么在服务内的每个方法上应用 async-await 以使其成为非阻塞方法是否存在任何问题。我问这个是否对 dbContext 实例使用 async-await 有任何问题,所以,例如,我会有这样的事情:

public async MyModel GetMyModel()
{
   var result = //await on a new task which will use dbcontext instance here
   return Mapper.Map<MyModel>(result);
}

非常感谢任何建议或建议!

【问题讨论】:

  • 稍微切线您的MyService 示例是使用服务定位器作为并列显式依赖原则。
  • @Nikosi 是的,我理解,但仅在涉及 dbContext 时使用这种方式是否可以接受?
  • 由于紧耦合,通常不建议这样做。尽管针对核心,您还应该阅读有关 DI docs.microsoft.com/en-us/aspnet/core/fundamentals/… 的文档中的这篇文章,重点关注 llifetime 范围部分
  • 如果MyService 依赖于dbContext 并且注册的生命周期比dbContext 更长,那么dbContext 有效注册的生命周期比MyService 更长。例如如果dbContext 是每个请求且MyService 是单例,则dbContext 被捕获,并且相对于MyService 而言实际上是单例
  • 请注意,对DependencyResolver.Current.GetService&lt;IDbContext&gt; 的调用是Service Locator anti-pattern(Mark 的博客)的实现。你应该总是更喜欢构造函数注入。

标签: c# entity-framework dependency-injection asp.net-mvc-5 autofac


【解决方案1】:

我会从远处解决问题。

有一些架构选择可以让您的生活更轻松。在 Web 开发中,将您的应用程序设计为具有 无状态服务层(所有状态都保存在 DB 中)并符合 一个 HTTP 请求,一个业务操作 原则是很实用的(换句话说,一种控制器操作的一种服务方法)。

我不知道您的架构看起来如何(您的帖子中没有足够的信息来确定),但它很可能符合我上面描述的标准。

在这种情况下,很容易决定选择哪个组件生命周期:DbContext 和服务类可以是瞬态的(Autofac 术语中的 InstancePerDependency)或每个请求的(InstancePerRequest) - 这并不重要。关键是它们具有相同的生命周期,因此根本不会出现强制依赖的问题

上述的进一步含义:

  • 您可以在您的服务类中使用 ctor 注入而无需担心。 (无论如何,在调查了诸如lifetime scopes and IOwned<T> 这样的生命周期控制可能性之后,服务定位器模式将是最后的选择。)

  • EF 本身通过 SaveChanges 实现工作单元模式,这适用于大多数情况。实际上,如果它的事务处理由于某种原因不能满足您的需求,您只需要在 EF 上实现 UoW。这些是相当特殊的情况。

[...] 在内部的每个方法上应用 async-await 是否有任何问题 服务使它们成为非阻塞方法。

如果您始终如一地应用异步等待模式(我的意思是等待所有异步操作)直接到您的控制器操作(返回 Task 而不是 ActionResult ),不会有任何问题。 (但是,请记住,在 ASP.NET MVC 5 中,异步支持并不完整 - async child actions are not supported。)

【讨论】:

    【解决方案2】:

    与往常一样,答案是否取决于...如果满足以下条件,此配置可以工作:

    1. 您的范围是在请求边界内创建的。您的工作单元是否在创建范围?
    2. 在创建范围之前,您不会解析任何 InstancePerLifetimeScope 服务。否则,如果您在请求中创建多个范围,它们的寿命可能会比应有的更长。

    我个人只建议制作任何依赖于 DbContext(直接或间接)的 InstancePerRequest。瞬态也可以。您肯定希望一个工作单元中的所有内容都使用相同的 DbContext。否则,使用 Entity Framework 的一级缓存,您可能有不同的服务检索相同的数据库记录,但如果它们不使用相同的 DbContext,则它们会在不同的内存副本上运行。在这种情况下,最后一次更新将获胜。

    我不会在 MyService 中引用你的容器,只是构造函数注入它。您的域或业务逻辑中的容器引用应谨慎使用,并且仅作为最后的手段。

    【讨论】:

    • IUnitOfWorkManager 也是每个生命周期的实例。我解决IUnitOfWork 的方法只是在每次使用时提供一个新实例:` public IUnitOfWork NewUnitOfWork() { return new UnitOfWork(_context); }`
    猜你喜欢
    • 2016-01-10
    • 1970-01-01
    • 1970-01-01
    • 2010-12-16
    • 2011-10-06
    • 2013-05-08
    • 1970-01-01
    • 2016-09-29
    • 2016-08-10
    相关资源
    最近更新 更多