G’day guys!
Today I thought I’d take a break from the security themed posts I’ve been writing lately and cover how Microsoft’s Dependency Injection (DI) container works with automatically disposing services. Specifically, I wanted to highlight the scenarios in which the DI container will dispose of your services for you and the scenario in which it won’t.
Two bits of assumed knowledge here are the Dispose and Dependency Injection patterns in the .NET ecosystem.
Typed registration
Let’s start with the scenario that everybody’s seen a million times. If we register a service in the DI container like so:
builder.Services.AddSingleton<IService, Service>();
And Service
also implements the IDisposable
interface, will Dispose()
get called when the application shuts down?
According to https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-guidelines, The container is responsible for cleanup of types it creates, and calls Dispose on IDisposable instances.
Since the container creates the instance when it’s required, it makes sense that it calls Dispose()
when the application shuts down. This can be verified by throwing a good old Console.WriteLine(...)
in the Dispose()
method of Service
.
But what if you need to register a singleton instance of something with certain configuration, and don’t want to inject a factory? That is, you want to register an instance of IService
, not IServiceFactory
that contains a GetService
method. For example, the IConnectionMultiplexer
from StackExchange.Redis or ElasticsearchClient
from ElasticSearch.
Created registration
Your first thought might be to create the object in your DI setup and simply register the created object, like so:
var service = new Service();
builder.Services.AddSingleton<IService>(service);
Now let’s ask the same question. Will Dispose()
get called when the application shuts down?
Unfortunately, no. Since the DI container did not create the instance, it will not be disposed of. This can potentially be a problem, since it means that files may not be closed, memory may not be freed and other clean up tasks did not run.
Factory registration
There is one alternative which I think solves this problem, which is to inject a delegate factory that instructs the DI container on how to create the instance we want, but it’s still being created by the DI container.
builder.Services.AddSingleton<IService>((provider) =>
{
return new Service();
});
From the documentation, If a type or factory is registered as a singleton, the container disposes the singleton automatically. If we add another good old Console.WriteLine(...)
to our Service
, we can indeed see that Dispose()
is being called when the application shuts down.
One last gotcha
If you’re testing this at home, one last little gotcha to mention is that closing the browser window or hitting the stop button in Visual Studio will kill the process and the application will not perform a graceful shutdown - therefore you won’t notice any disposal taking place. To perform a graceful shutdown, hit Ctrl + C
in the terminal window that launched your application instead.
Alrighty guys, that’s about it for today. Until next time, Happy Coding and Happy Disposing!
Catch ya!