One of the most commonly implemented interfaces in .NET is the IDisposable
interface. Classes implement IDisposable
when they contain references to unmanaged resources, such as window handles, files or sockets. The garbage collector automatically releases memory for managed (i.e. .NET) objects, but it doesn't know about how to handle the unmanaged resources. Implementing IDisposable
provides a hook, so you can properly clean up those resources when your class is disposed.
This post goes over some of the options available to you for disposing services in ASP.NET Core applications, especially when using the built-in dependency injection container.
For the purposes of this post, I'll use the following class that implements IDisposable
in the examples. I'm just writing to the console instead of doing any actual cleanup, but it'll serve our purposes for this post.
public class MyDisposable : IDisposable
{
public MyDisposable()
{
Console.WriteLine("+ {0} was created", this.GetType().Name);
}
public void Dispose()
{
Console.WriteLine("- {0} was disposed!", this.GetType().Name);
}
}
Now let's look at our options.
The simple case - a using
statement
The typical suggested approach when consuming an IDisposable
in your code, is with a using
block:
using(var myObject = new MyDisposable())
{
// myObject.DoSomething();
}
Using IDisposable
s in this way ensures they are disposed correctly, whether or not they throw an exception. You could also use a try
-finally
block instead if necessary:
MyDisposable myObject;
try
{
myObject = new MyDisposable();
// myObject.DoSomething();
}
finally
{
myObject?.Dispose();
}
You'll often find this pattern when working with files or streams - things that you only need transiently, and are finished with in the same scope. Unfortunately, sometimes this won't suit your situation, and you might need to dispose of the object from somewhere else. Depending on your exact situation, there are a number of other options available to you.
Note: Wherever possible, it's best practice to dispose of objects in the same scope they were created. This will help prevent memory leaks and unexpected file locks in your application, where objects go accidentally undisposed.
Disposing at the end of a request - using RegisterForDispose
When you're working in ASP.NET Core, or any web application, it's very common for your objects to be scoped to a single request. That is, anything you create to handle a request you want to dispose when the request finishes.
There are a number of ways to do this. The most common way is to leverage the DI container which I'll come to in a minute, but sometimes that's not possible, and you need to create the disposable in your own code.
If you are manually creating an instance of an IDisposable
, then you can register that disposable with the HttpContext
, so that when the request ends, the instance will be disposed automatically. Simply pass the instance to HttpContext.Response.RegisterForDispose
:
public class HomeController : Controller
{
readonly Disposable _disposable;
public HomeController()
{
_disposable = new RegisteredForDispose();
}
public IActionResult Index()
{
// register the instance so that it is disposed when request ends
HttpContext.Response.RegisterForDispose(_disposable);
Console.Writeline("Running index...");
return View();
}
}
In this example, I'm creating the Disposable
during the constructor of the HomeController
, and then registering for its disposal in the action method. This is a little bit contrived, but it shows the mechanism at least.
If you execute this action method, you'll see the following:
$ dotnet run
Hosting environment: Development
Content root path: C:\Users\Sock\Repos\RegisterForDispose
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.
+ MyDisposable was created
Running index...
- MyDisposable was disposed!
The HttpContext
takes care of disposing our object for us!
Warning: I registered the instance in the action method, instead of the constructor method because
HttpContext
might benull
in the constructor!
RegisterForDispose
is useful when you are new
-ing up services in your code. But given that Dispose is only required for classes using unmanaged resources, you'll probably find that more often than not, your IDisposable
classes are encapsulated in services that are registered with the DI container.
As Mark Rendle pointed out, the
Controller
itself will also be disposed at the end of the request, so you can use that mechanism to dispose any objects you create.
Automatically disposing services - leveraging the built-in DI container
ASP.NET Core comes with a simple, built-in DI container that you can register your services with as either Transient, Scoped, or Singleton. You can read about it here, so I'll assume you already know how to use it to register your services.
Note, this article only discusses the built-in container - third-party containers might have other rules around automatic disposal of services.
Any service that the built-in container creates in order to fill a dependency, which also implements IDisposable
, will be disposed by the container at the appropriate point. So Transient
and Scoped
instances will be disposed at the end of the request (or more accurately, at the end of a scope), and Singleton
services will be disposed when the application is torn down and the ServiceProvider
itself is disposed.
To clarify, that means the provider will dispose any service you register with it, as long as you don't provide a specific instance. For example, I'll create a number of disposable classes:
public class TransientCreatedByContainer: MyDisposable { }
public class ScopedCreatedByFactory : MyDisposable { }
public class SingletonCreatedByContainer: MyDisposable {}
public class SingletonAddedManually: MyDisposable {}
And register each of them in a different way in Startup.ConfigureServices
. I am registering
TransientCreatedByContainer
as a transientScopedCreatedByFactory
as scoped, using a lambda function as a factorySingletonCreatedByContainer
as a singletonSingletonAddedManually
as a singleton by passing in a specific instance of the object.
public void ConfigureServices(IServiceCollection services)
{
// other services
// these will be disposed
services.AddTransient<TransientCreatedByContainer>();
services.AddScoped(ctx => new ScopedCreatedByFactory());
services.AddSingleton<SingletonCreatedByContainer>();
// this one won't be disposed
services.AddSingleton(new SingletonAddedManually());
}
Finally, I'll inject an instance of each into the HomeController
, so the DI container will create / inject instances as necessary:
public class HomeController : Controller
{
public HomeController(
TransientCreatedByContainer transient,
ScopedCreatedByFactory scoped,
SingletonCreatedByContainer createdByContainer,
SingletonAddedManually manually)
{ }
public IActionResult Index()
{
return View();
}
}
When I run the application, hit the home page, and then stop the application, I get the following output:
$ dotnet run
+ SingletonAddedManually was created
Content root path: C:\Users\Sock\Repos\RegisterForDispose
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.
+ TransientCreatedByContainer was created
+ ScopedCreatedByFactory was created
+ SingletonCreatedByContainer was created
- TransientCreatedByContainer was disposed!
- ScopedCreatedByFactory was disposed!
Application is shutting down...
- SingletonCreatedByContainer was disposed!
There's a few things to note here:
SingletonAddedManually
was created before the web host had finished being set up, so it writes to the console before logging startsSingletonCreatedByContainer
is disposed after we have started shutting down the serverSingletonAddedManually
is never disposed as it was provided a specific instance!
Note the behaviour whereby only objects created by the DI container are disposed applies to ASP.NET Core 1.1 and above. In ASP.NET Core 1.0, all objects registered with the container are disposed.
Letting the container handle your IDisposable
s for you is obviously convenient, especially as you are probably registering your services with it anyway! The only obvious hole here is if you need to dispose an object that you create yourself. As I said originally, if possible, you should favour a using
statement, but that's not always possible. Luckily, ASP.NET Core provides hooks into the application lifetime, so you can do some clean up when the application is shutting down.
Disposing when the application ends - hooking into IApplicationLifetime
events
ASP.NET Core exposes an interface called IApplicationLifetime
that can be used to execute code when an application is starting up or shutting down:
public interface IApplicationLifetime
{
CancellationToken ApplicationStarted { get; }
CancellationToken ApplicationStopping { get; }
CancellationToken ApplicationStopped { get; }
void StopApplication();
}
You can inject this into your Startup
class (or elsewhere) and register to the events you need. Extending the previous example, we can inject both the IApplicationLifetime
and our singleton SingletonAddedManually
instance into the Configure
method of Startup.cs:
public void Configure(
IApplicationBuilder app,
IApplicationLifetime applicationLifetime,
SingletonAddedManually toDispose)
{
applicationLifetime.ApplicationStopping.Register(OnShutdown, toDispose);
// configure middleware etc
}
private void OnShutdown(object toDispose)
{
((IDisposable)toDispose).Dispose();
}
I've created a simple helper method that takes the state passed in (the SingletonAddedManually
instance), casts it to an IDisposable
, and disposes it. This helper method is registered with the CancellationToken
called ApplicationStopping
, which is fired when closing down the application.
If we run the application again, with this additional registration, you can see that the SingletonAddedManually
instance is now disposed, just after the application shutting down trigger.
$ dotnet run
+ SingletonAddedManually was created
Content root path: C:\Users\Sock\Repos\RegisterForDispose
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.
+ TransientCreatedByContainer was created
+ ScopedCreatedByFactory was created
+ SingletonCreatedByContainer was created
- TransientCreatedByContainer was disposed!
- ScopedCreatedByFactory was disposed!
Application is shutting down...
- SingletonAddedManually was disposed!
- SingletonCreatedByContainer was disposed!
Summary
So there you have it, four different ways to dispose of your IDisposable
objects. Wherever possible, you should either use the using
statement, or let the DI container handle disposing objects for you. For cases where that's not possible, ASP.NET Core provides two mechanisms you can hook in to: RegisterForDispose
and IApplicationLifetime
.
Oh, and apparently I missed one final way:
He forgot one… unplug the server / cloud. ALL THINGS R DISPOSED.
— Khalid Abuhakmeh (@buhakmeh) 21 June 2017