Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do we really need to implement IDisposable in Repository or UnitOfWork classes?

First, let's see what Microsoft says about Asp.Net Core's default Dependency Injection services:

The framework takes on the responsibility of creating an instance of the dependency and disposing of it when it's no longer needed.

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

I.e. the framework would be calling a classes Dispose method (assuming the class implements IDisposable)

Second, the DbContext class does indeed implement IDisposable out of the box.

Third, in our Startup.cs class, we add our DbContext via the AddDbContext method, which by default gets added as a Scoped instance (i.e. our DbContext is created and garbage collected on each single request).

Scoped lifetime services are created once per request.

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

E.g.

public void ConfigureServices(IServiceCollection services)
{
    services
        .AddDbContext<TheStoreDbContext>(ConfigureDbContext)      
        .AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2) 
}

Conclusion, we don't need to explicitly call context.Dispose() anywhere in our Asp.net Core application.

So why are there so many examples online and in tutorials which show you that you have to implement IDisposable in your Repository or UnitOfWork classes?

E.g.

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();
        }
    }

What do you think? Is this a valid question? Does it make sense not to explicitly call the Dispose() method anywhere?

like image 281
Francisco Vilches Avatar asked Jan 29 '19 09:01

Francisco Vilches


1 Answers

The rule of thumb is that a class should only dispose what it owns. In the case of the UnitOfWork, however, it does not own the DbContext, as it is not created by UnitOfWork—instead it provided from the outside by someone else.

Letting UnitOfWork dispose of that DbContext can even be problematic, because UnitOfWork has no way of knowing whether or not DbContext is still used by other classes in the system when UnitOfWork is disposed of. In other words, the application might break when UnitOfWork starts disposing that dependency.

So if you follow the rule of only disposing what you own, it means that UnitOfWork should in fact not implement IDisposable at all. And this means that you let 'someone else' control the lifetime of the DbContext.

This idea of moving the control over the lifetime of dependencies (such as DbContext) to a third party is not something new, and certainly not something specific to the new Microsoft DI container. This is in fact an old idea, which is the idea that you centralize the control over the lifetime of application components in the start-up path of the application. In the context of DI, this start-up path is typically referred to as the Composition Root.

Inside the Composition Root, it will be the job of the Composer to create application components, manage their lifetime, and dispose of them when they are not needed any longer. A DI Container (like the .NET Core DI Container) acts as the Composer in your system, but you can do this just as well by hand—a practice commonly known as Pure DI.

Also see this answer, which also talks about disposing in the context of the SOLID principles.

like image 127
Steven Avatar answered Sep 18 '22 23:09

Steven