我们是否真的需要在 Repository 或 UnitOfWork 类中实现 IDisposable 接口?

15

首先,让我们看看微软关于Asp.Net Core默认依赖注入服务的说法:

框架负责创建依赖项的实例,并在不再需要时处理它。

https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.1#disposal-of-services

也就是说,框架将调用类的Dispose方法(假设该类实现了IDisposable接口)。

其次,DbContext类确实从一开始就实现了IDisposable接口。

第三,在我们的Startup.cs类中,我们通过AddDbContext方法添加我们的DbContext,默认情况下,它作为Scoped实例添加(即我们的DbContext在每个单独请求上都会被创建和垃圾回收)。

Scoped生命周期服务是每个请求创建一次。

https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.1#service-lifetimes

例如:

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()方法有意义吗?


3
你必须这么做吗?不,我出于同样的原因不这么做——我让框架进行清理,但是我使用ASP.NET Core的应用程序并不关注性能或内存限制。如果你有更多事情要处理,那么实现IDisposable来释放资源是很好的做法。将清理交给框架是一种良好的实践吗?在C++中可以这么做吗?其实不然,随着时间推移,你会遇到问题。 - ColinM
3
如果您使用 DI,通常会让 DI 框架为您处理对象的释放。您需要设置正确的生命周期以防止内存泄漏,因此我总是避免这种情况。但是,任何非注入依赖项都应该由您自己清理。Dispose 调用仍然很重要,只是取决于谁调用它,您还是 DI 框架。 - Liam
2
可能是如何使用依赖注入释放资源的重复问题。 - Liam
2
简而言之,如果一个依赖关系实现了“IDisposable”,则您应始终调用“Dispose”。 如何操作取决于您。 - Liam
2
虽然我确信(正如ColinM的评论所暗示的那样),确实存在性能关键的情况,您需要手动处理DbContext实例,但人们这样做的“原因”通常是因为它只是盲目地从其他人那里复制而来的,就像货物崇拜一样 :) 此外,关于UoW / Repository示例 - DbContext已经提供了它(上下文是工作单元,DBSet <T>是存储库),因此您在问题中看到的任何示例可能并不一定代表最佳实践 :) - Stephen Byrne
显示剩余3条评论
1个回答

20
经验法则是一个类只应该处理它“拥有”的东西。然而,在你的UnitOfWork类的情况下,它并不“拥有”DbContext,因为它不是由UnitOfWork创建的,而是由“其他人”从外部提供的。
让UnitOfWork处理那个DbContext甚至可能会有问题,因为UnitOfWork在被处理时无法知道DbContext是否仍然被系统中的其他类使用。换句话说,当UnitOfWork开始处理该依赖项时,应用程序可能会出现问题。
因此,如果你遵循只处理你“拥有”的东西的规则,这意味着UnitOfWork实际上根本不应该实现IDisposable。这意味着你让“其他人”控制DbContext的生命周期。
这个将依赖项(如DbContext)的生命周期控制移交给第三方的想法并不新鲜,也不是仅限于新的Microsoft DI容器。实际上,这是一个古老的想法,即将应用程序组件的生命周期控制集中在应用程序的启动路径中。在DI的上下文中,这个启动路径通常被称为组合根
在组合根中,组合器的工作是创建应用程序组件,管理它们的生命周期,并在不再需要时将它们销毁。一个DI容器(如.NET Core DI容器)在您的系统中充当组合器的角色,但您也可以通过手动方式来实现这一点,这被称为纯DI的常见做法。
还可以参考这个答案,它也讨论了在SOLID原则的上下文中的销毁问题。

这并不是一种不好的实践,因为你怎么知道你是否需要处理你的依赖关系呢?依赖注入是一种不好的实践,因为最佳实践是每个类都必须释放其依赖项,但在依赖注入中,如果你没有这样做,你将会出现内存溢出,因为在像数据库上下文这样的IO操作中,某人必须调用disposable,问题是谁? - Ali Yousefi
这个示例是在流上工作的,如果您调用主类的dispose方法,那么它也会处理子流。这与工作单元示例不同。 MemoryStream memoryStream = new MemoryStream(); var writer = new StreamWriter(memoryStream); writer.Dispose(); //抛出异常 memoryStream.WriteByte(1); - Ali Yousefi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接