The Disposable Pattern in ASP.NET Core

Introduction

When you use a class that implements the Dispose Pattern, or, in general, that implements IDisposable, you should know what to do with it, in regards to its lifetime. We typically want to keep it as short as possible, but that may not always be the case; in any case, we definitely want to dispose of it properly. Let's see some of the issues around this.

Instantiation

There are two ways by which we can instantiate our IDisposable objects:

  • Using Dependency Injection (DI)
  • Manually

When using DI, we have three different possible lifetimes, which require different cares.

DI Instantiation

Scoped Disposal

If we are to use DI, then, chances are, we will make use of the Scoped lifetime. This essentially means that the framework, at the end of the scope - the web request - will call its Dispose method automatically. Nothing really to be done.

Transient Disposal

For Transient instances registered on the DI container, we should create a scope on the DI container, and dispose of it, after which, all instances created by that scope will be disposed too:

using var scope = serviceProvider.CreateScope();

var myService = scope.ServiceProvider.GetService<IMyService>();

//no need to explicitly dispose transient instances created on a scope that is disposed of

Singleton Disposal

But what if we created the instance ourselves, or if they are created by DI with a Singleton lifetime? Then, we need to find a proper process for its disposal.

Manual Instantiation

Controller-level Disposal

If we manually created our instance inside a Controller, or, it uses the Singleton lifetime, then, we can choose to dispose of it in an overriden Dispose method, as it also implements IDisposable:

private IMyType _myService;

protected override void Dispose(bool disposing)

{

if (disposing)

{

_myService?.Dispose();

_myService = null;

}

base.Dispose(disposing);

}

This will be called when the controller is done, somewhere close to the end of the request.

Response-level Disposal

Another option would be to register our instance with the current response for disposing. This is achieved through the HttpResponse.RegisterForDispose method:

HttpContext.HttpResponse.RegisterForDispose(myService);

This works if we need our instance to outlive its instantiating method but still want it gone by the end of the request.

Application-level Disposal

The final option I'm going to cover is to dispose of a manually-instantiated or Singleton when the application is about to stop. For that we need to hook to the IHostApplicationLifetime.ApplicationStopping cancellation token:

var appLifetime = serviceProvider.GetRequiredService<IHostApplicationLifetime>();
appLifetime.ApplicationStopping.Register(() =>
{
myService?.Dispose();
});

The Register method takes a lambda where we can do pretty much what we want, in this case, we are disposing of a local variable. ApplicationStopping will be triggered when the ASP.NET Core application is about to stop. You should prefer this over ApplicationStopped.

Conclusion

I hope I could explain something new about disposable types and ASP.NET Core. It may seem like a simple matter, but it isn't really. See more tips here.

Comments

Popular posts from this blog

Audit Trails in EF Core

.NET Cancellation Tokens

Domain Events with .NET