首先,我们来看看微软对于Asp.Net Core默认的依赖注入服务是怎么说的:
框架负责创建依赖项实例并在不再需要时将其处置。
即框架将调用类 Dispose 方法(假设该类实现 IDisposable)
第二,DbContext 类确实实现了开箱即用的 IDisposable。
第三,在 Startup.cs 类中,我们通过 AddDbContext 方法添加 DbContext,默认情况下该方法会添加为 Scoped 实例(即我们的 DbContext 在每个请求上创建并进行垃圾收集)。
每个请求都会创建一次范围内的生命周期服务。
例如
public void ConfigureServices(IServiceCollection services)
{
services
.AddDbContext<TheStoreDbContext>(ConfigureDbContext)
.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
}
结论,我们不需要在 Asp.net Core 应用程序中的任何地方显式调用 context.Dispose() 。
那么为什么网上和教程中有这么多示例表明您必须在 Repository 或 UnitOfWork 类中实现 IDisposable ?
例如
public class UnitOfWork : IUnitOfWork
{
private readonly DbContext _context;
public IProductRepository ProductRepository { get; }
public UnitOfWork(DbContext context)
{
_context = context;
ProductRepository = new ProductRepository(context);
}
public void Dispose()
{
_context.Dispose();
}
}
你觉得怎么样?这是一个有效的问题吗?在任何地方都不显式调用 Dispose() 方法是否有意义?
经验法则是,一个类应该只处理它拥有的。然而,就您的
UnitOfWork
类而言,它并不 拥有 DbContext
,因为它不是由 UnitOfWork
创建的,而是由“其他人”从外部提供。让 DbContext
处理
UnitOfWork
甚至可能会产生问题,因为当 DbContext
被处理时,UnitOfWork
无法知道系统中的其他类是否仍在使用 DbContext
。换句话说,当 UnitOfWork
开始处理该依赖项时,应用程序可能会中断。因此,如果您遵循仅处置您拥有的的规则,则意味着UnitOfWork
实际上应该
不实施
UnitOfWork
。这意味着您让“其他人”控制 IDisposable
的生命周期。将依赖项生命周期的控制权(例如DbContext
)转移给第三方的想法并不是什么新鲜事,当然也不是新的 Microsoft DI 容器特有的东西。这实际上是一个古老的想法,即在应用程序的启动路径中集中控制应用程序组件的生命周期。在 DI 上下文中,此启动路径通常称为
Composition Root。 在组合根内部,Composer
的工作是创建应用程序组件、管理它们的生命周期,并在不再需要它们时将其丢弃。 DI 容器(如 .NET Core DI 容器)充当系统中的 Composer,但您也可以手动完成此操作,这种做法通常称为“纯 DI”。 另请参阅这个答案,其中也谈到了在 SOLID 原则的背景下进行处置。