Cristopher Coronado
What is the best way to ensure a Disposable object is cleaned up?
By Cristopher Coronado in .NET on Aug 05 2020
  • Rajesh Gami
    Aug, 2020 7

    Four ways to dispose IDisposables in ASP.NET Core

    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.

    1. public class MyDisposable : IDisposable
    2. {
    3. public MyDisposable()
    4. {
    5. Console.WriteLine("+ {0} was created", this.GetType().Name);
    6. }
    7. public void Dispose()
    8. {
    9. Console.WriteLine("- {0} was disposed!", this.GetType().Name);
    10. }
    11. }

    The simple case - a using statement
    The typical suggested approach when consuming an IDisposable in your code, is with a using block:

    1. using(var myObject = new MyDisposable())
    2. {
    3. // myObject.DoSomething();
    4. }

    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:

    1. MyDisposable myObject;
    2. try
    3. {
    4. myObject = new MyDisposable();
    5. // myObject.DoSomething();
    6. }
    7. finally
    8. {
    9. myObject?.Dispose();
    10. }

    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:

    1. public class HomeController : Controller
    2. {
    3. readonly Disposable _disposable;
    4. public HomeController()
    5. {
    6. _disposable = new RegisteredForDispose();
    7. }
    8. public IActionResult Index()
    9. {
    10. // register the instance so that it is disposed when request ends
    11. HttpContext.Response.RegisterForDispose(_disposable);
    12. Console.Writeline("Running index...");
    13. return View();
    14. }
    15. }

    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:

    1. $ dotnet run
    2. Hosting environment: Development
    3. Content root path: C:\Users\Sock\Repos\RegisterForDispose
    4. Now listening on: http://localhost:5000
    5. Application started. Press Ctrl+C to shut down.
    6. + MyDisposable was created
    7. Running index...
    8. - 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:

    1. public class TransientCreatedByContainer: MyDisposable { }
    2. public class ScopedCreatedByFactory : MyDisposable { }
    3. public class SingletonCreatedByContainer: MyDisposable {}
    4. public class SingletonAddedManually: MyDisposable {}

    And register each of them in a different way in Startup.ConfigureServices. I am registering

    1. TransientCreatedByContainer as a transient
    2. ScopedCreatedByFactory as scoped, using a lambda function as a factory
    3. SingletonCreatedByContainer as a singleton
    4. SingletonAddedManually as a singleton by passing in a specific instance of the object.
    1. public void ConfigureServices(IServiceCollection services)
    2. {
    3. // other services
    4. // these will be disposed
    5. services.AddTransient<TransientCreatedByContainer>();
    6. services.AddScoped(ctx => new ScopedCreatedByFactory());
    7. services.AddSingleton<SingletonCreatedByContainer>();
    8. // this one won't be disposed
    9. services.AddSingleton(new SingletonAddedManually());
    10. }

    Finally, I’ll inject an instance of each into the HomeController, so the DI container will create / inject instances as necessary:

    1. public class HomeController : Controller
    2. {
    3. public HomeController(
    4. TransientCreatedByContainer transient,
    5. ScopedCreatedByFactory scoped,
    6. SingletonCreatedByContainer createdByContainer,
    7. SingletonAddedManually manually)
    8. { }
    9. public IActionResult Index()
    10. {
    11. return View();
    12. }
    13. }

    When I run the application, hit the home page, and then stop the application, I get the following output:

    1. $ dotnet run
    2. + SingletonAddedManually was created
    3. Content root path: C:\Users\Sock\Repos\RegisterForDispose
    4. Now listening on: http://localhost:5000
    5. Application started. Press Ctrl+C to shut down.
    6. + TransientCreatedByContainer was created
    7. + ScopedCreatedByFactory was created
    8. + SingletonCreatedByContainer was created
    9. - TransientCreatedByContainer was disposed!
    10. - ScopedCreatedByFactory was disposed!
    11. Application is shutting down...
    12. - 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:

    1. public interface IApplicationLifetime
    2. {
    3. CancellationToken ApplicationStarted { get; }
    4. CancellationToken ApplicationStopping { get; }
    5. CancellationToken ApplicationStopped { get; }
    6. void StopApplication();
    7. }

    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:

    1. public void Configure(
    2. IApplicationBuilder app,
    3. IApplicationLifetime applicationLifetime,
    4. SingletonAddedManually toDispose)
    5. {
    6. applicationLifetime.ApplicationStopping.Register(OnShutdown, toDispose);
    7. // configure middleware etc
    8. }
    9. private void OnShutdown(object toDispose)
    10. {
    11. ((IDisposable)toDispose).Dispose();
    12. }

    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.

    1. $ dotnet run
    2. + SingletonAddedManually was created
    3. Content root path: C:\Users\Sock\Repos\RegisterForDispose
    4. Now listening on: http://localhost:5000
    5. Application started. Press Ctrl+C to shut down.
    6. + TransientCreatedByContainer was created
    7. + ScopedCreatedByFactory was created
    8. + SingletonCreatedByContainer was created
    9. - TransientCreatedByContainer was disposed!
    10. - ScopedCreatedByFactory was disposed!
    11. Application is shutting down...
    12. - SingletonAddedManually was disposed!
    13. - SingletonCreatedByContainer was disposed!

    • 1
  • Varun Setia
    Aug, 2020 6

    We should perform the auto-dispose through using statement using(var conn=new Connection()){}

    • 0


Most Popular Job Functions


MOST LIKED QUESTIONS