避免用using包装DbContext【翻译】

   EF和EF Core 的DbContext类实现IDisposable接口。所以,不少最佳编程实践中都建议你将它们放在一个using()块中。不幸的是,至少在Web应用程序中,这样作一般不是一个好主意。
   我与许多从.NET Framework迁移到.NET Core和.NET 5的客户一块儿工做,其中一些客户在旧版应用程序中并无使用依赖项注入,或者没有一直使用它。结果致使他们的DbContext类有大量的实例。这样作有不少问题,其中最重要的是它致使了紧耦合。
                  “在Web应用程序中,每一个Web请求应该有且只有一个DbContext。”
   若是您遵循上述规则,则一切正常。不然可能会有不少麻烦。好比你会遇到这样的问题:如何跟踪实体,或被跟踪的实体在你认为应保存时并无保存(稍后对此进行详细介绍)。尤为是若是使用异步代码,你可能会发现DbContext被释放的意外状况,这可能须要一些时间来解决。
                  “在ASP.NET或ASP.NET Core中,配置DbContext最理想的方法是经过DI容器。”
   若是让DI容器(如Autofac)帮你管理DbContext实例及其生命周期,就能够避免以上全部这些麻烦。若是您还使用仓储或相似的抽象,请确保它们的生命周期与DbContext的生命周期一致。ASP.NET Core内置的DI容器和Helper能在Scoped 生命周期类型中为EF Core作出正确的配置。这意味着每一个请求将建立一个新的DbContext实例。且这个请求中,任何想使用DbContext实例的操做都共享同一实例。在请求结束时将其清理并释放。若是您使用的是Autofac和EF 6,Scoped就是每一个请求只使用一个实例的意思。数据库

using语句和DbContext

   使用using语句可能的问题:编程

  • 您释放了一个DbContext,使得一个实体没法保存
  • 您异步地把DbContext传递给另外一个服务,可是在它被(另外一个服务)使用前,就在原始服务中的using块中将其释放
  • 当同时当使用构造函数注入并在ConfigureServices 或者 一个Autofac 模块中添加一行代码这种正确的的行为时(即同时使用using和容器管理),整个应用程序范围内显得代码(没必要要的)重复和混乱。

多个DbContext的问题

   与using语句和DbContext密切相关的问题是:若是有多个DbContext(由于该using语句一般会建立一个新实例)。
                “若是您有多个DbContext尝试使用相同的实体实例,那么您将承受巨大的痛苦。”
   考虑这个简单的示例,它包含了一个Controller和一个service,二者同时在使用dbContext。app

   假设在Index方法中,从数据库中读取 starship时,它的name属性值为“ Millenium Falcon”。下次访问Index时,name属性值是什么?异步

   SaveChanges()被调用时不执行任何操做。db实例在StarshipService中并未跟踪该实体。因此名称将保持不变。
   那么,咱们能够这样解决这个问题。让咱们附加实体。这是更新后的service:函数

   再次运行。如今Name的值是什么?spa

   SaveChanges()仍然不执行任何操做。实体的name被更新,该实体依然未跟踪。
   让咱们再尝试一次:翻译

   如今,它会起做用吗?你以为如何?对象

   是的,如今name的值已更新为“ Millenium Falcon *”,下次是“ Millenium Falcon **”,等等。blog

   要正确地获得预期的行为,须要进行大量的尝试工做。这段代码是脆弱且重复的,更糟的是,具备隐藏的暂存依赖性。接口

   那么,MVC5/EF6的旧代码中要怎么使用Autofac呢?

安装Nuget软件包

   安装Autofac.MVC5 nuget软件包。与.NET Framework nuget包不一样,它不会添加一堆类,但你仍然须要将它们链接起来。
   进到global.asax文件中更新Application_Start():

   当你成功运行它,你还能够把它们放置到一个模块中,并把它的helper放到在App_Start目录中。

   这部分代码中所作的事情有: 

  • 配置Autofac的依赖项容器
  • 设置它,以建立controller
  • 设置它,以建立 ApplicationDbContext
  • 设置它,以建立 StarshipService
  • 使用 InstancePerRequest设置上面两个
  • 配置MVC5以使用Autofac来解决其依赖性

   这可能也就须要用5分钟。更新以后还会有混乱的代码吗?好了,服务如今看起来像这样:

   另外,控制器如今看起来像这样:

   请注意,这两种类型如今都遵循显式依赖项原则。同时注意到,在解耦以后的代码没彻底没有new关键字,这与显式依赖原则有关。

                “方法和类应显式要求(一般经过方法参数或构造函数参数)它们所须要的协做对象才能正常运行。”

   不用对所引用对象的隐式依赖感到惊讶。若是须要引用某个类,应该在构造函数中引入它(注:依赖注入)。若是要将类进行解耦,,则应该声明一个抽象类(或接口),而不是直接使用new建立实例或者调用静态方法。

总结

   EF和EF Core能够为您节省大量时间,并使你更容易地关注领域模型而不是底层的数据库问题。可是若是没有正确使用它们,当你尝试找出它们出现异常的缘由时,它们也会让你很头痛。避免直接实例化和避免using块都将使您的代码更容易使用。对于ASP.NET(Core)应用程序,应确保每一个请求有且仅具备一个DbContext实例,而实现此目的的最佳方法是使用诸如Autofac之类的DI容器(或ASP. NET Core内置的 ServiceCollection)。

 

本文翻译自 https://ardalis.com/avoid-wrapping-dbcontext-in-using/

相关文章
相关标签/搜索