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); }}
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);
The simple case - a using statementThe typical suggested approach when consuming an IDisposable in your code, is with a using block:
using(var myObject = new MyDisposable()){ // myObject.DoSomething();}
using(var myObject = new MyDisposable())
// myObject.DoSomething();
Using IDisposables 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();}
MyDisposable myObject;
try
myObject = new MyDisposable();
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.
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(); }}
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 runHosting environment: DevelopmentContent root path: C:\Users\Sock\Repos\RegisterForDisposeNow listening on: http://localhost:5000Application started. Press Ctrl+C to shut down.+ MyDisposable was createdRunning index...- MyDisposable was disposed!
$ 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!
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.
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 {}
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.
TransientCreatedByContainer as a transient
ScopedCreatedByFactory as scoped, using a lambda function as a factory
SingletonCreatedByContainer as a singleton
SingletonAddedManually 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());}
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(); }}
public HomeController(
TransientCreatedByContainer transient,
ScopedCreatedByFactory scoped,
SingletonCreatedByContainer createdByContainer,
SingletonAddedManually manually)
{ }
When I run the application, hit the home page, and then stop the application, I get the following output:
$ dotnet run+ SingletonAddedManually was createdContent root path: C:\Users\Sock\Repos\RegisterForDisposeNow listening on: http://localhost:5000Application 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!
+ SingletonAddedManually was created
+ TransientCreatedByContainer was created
+ ScopedCreatedByFactory was created
+ SingletonCreatedByContainer was created
- TransientCreatedByContainer was disposed!
- ScopedCreatedByFactory was disposed!
Application is shutting down...
- SingletonCreatedByContainer was disposed!
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();}
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();}
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 createdContent root path: C:\Users\Sock\Repos\RegisterForDisposeNow listening on: http://localhost:5000Application 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!
- SingletonAddedManually was disposed!
We should perform the auto-dispose through using statement using(var conn=new Connection()){}