The DI container Microsoft.Extensions.DependencyInjection disposes registered services automatically. It’s just the question when the dispose takes place. Automatic dispose of transient and scoped services happen at the end of a scope. With ASP.NET Core applications, scopes are created with every HTTP request – after the request, services are disposed.
I’m using Microsoft.Extensions.DependencyInjection not only with ASP.NET Core and EF Core, but also with XAML-based applications (UWP, WPF, and Xamarin). Here it can be useful to create scopes with view-models that are associated with a XAML page. As soon as the page is not needed anymore, the scope is disposed and with it all the services from the current scope as well as all transient services. Singleton services are kept alive. Singletons are always associated with the root container – and only disposed when the root container is disposed.
This article shows creating and using DI scopes with XAML-based applications.
In case you use your own factory to pass service instances to the container, you still need to dispose the services yourself.
Service Registrations with Microsoft.Extensions.DependencyInjection
The container coming with .NET Core, Microsoft.Extensions.DependencyInjection, is deeply used with ASP.NET Core and EF Core. However, because it is shipped in a .NET Standard library, you can use it with other application types as well. Since the availability of NET Core, I’m using this DI container not only with ASP.NET Core, but also with WPF, UWP, and Xamarin applications.
In the UWP sample application, the container is created in the
AppServices class. Here, scoped, and transient services are registered.
- A transient service is instantiated every time an instance is requested.
- A singleton service is instantiated only once. On requests of this type, always the same instance is returned.
- With scoped registrations, an instance is created within every scope where the type is requested.
Transient service registration should be the preferred method to register services. Scoped and singleton services are useful to share state.
One more note to the lifetime of services:
A service injected should have the same or longer lifetime than the service where it is injected. For example, you can inject a singleton service within a transient service. The singleton service has a longer lifetime than the transient service. You cannot inject a transient service within a singleton. This would expand the lifetime of the transient service. Doing this with Microsoft.Extensions.DependencyInjection results in a exception.
What is a Scope?
With ASP.NET Core, DI scopes are created with every HTTP request. When a service is requested within a HTTP request (in middleware, controllers, services, or views), and the service is registered as scoped service, the same instance is used in case the same type is requested multiple times within a request. For example, if a scoped service is injected within a controller, a service, and within a view, the same instance is returned. With the flow of another HTTP request, a different instance is used. When the request is completed, the scope is disposed. Disposing the scope results in disposing of all transient and scoped services that are associated with that scope.
Singleton services are never associated with a scope – they are associated with the root container, and disposed when the root container is disposed.
Disposable Services and View-Models
Some services and view-models need to be disposed. For example, the
MasterDetailViewModel base class implements the interface
IDisposable to unsubscribe from events:
Injection of services into services and view-models
BooksService class needs an implementation of the
IBooksRepository interface. This is injected via constructor injection and assigned to a readonly member field. The base class of
BooksService, the abstract class
ItemsService needs some more services that are passed to the constructor of the base class.
Similar to the
BooksViewModel class defines services to be injected via the constructor:
Instantiating view-models in a scope
The view-model is associated to a page by assigning it to the
ViewModel property of the page. Here, a DI scope is created by invoking the
CreateScope method of the
ServiceProvider. This scope is disposed when the page is unloaded which is handled in the
Unloaded event handler method. Because the
BooksViewModel class is registered as transient service in the container, the instance is associated with the newly created scope, and disposed when the scope is disposed. All the transient and scoped services that are created directly or indirectly when the
BooksViewModel is instantiated, are disposed as well when the scope is disposed.
Disposing in Reverse Creation Order
In previous versions of the DI container, the DI container had a bug that is relevant if the disposing of services disposing services in the wrong order. This has been fixed with dispose services in reverse creation order.
See my ServicesLifetime sample of my book Professional C# 7 and .NET Core 2.0 creates multiple scopes with disposable services in a simple console application and uses the services.
Running the application, you can see that the services C and A are disposed when the scope is disposed – in the reverse creation order. Service A is a scoped service, and service B a transient service. Service B is disposed with the disposal of the root container – this service is registered as singleton:
UsingScoped ctor ServiceA, 1 A, 1 A, 1 ctor ServiceB, 2 B, 2 ctor ServiceC, 3 C, 3 ctor ServiceC, 4 C, 4 disposing ServiceC, 4 disposing ServiceC, 3 disposing ServiceA, 1 end of scope1 ctor ServiceA, 5 A, 5 B, 2 ctor ServiceC, 6 C, 6 disposing ServiceC, 6 disposing ServiceA, 5 end of scope2 disposing ServiceB, 2
Using dependency injection makes the code a lot more maintainable. You can easily see dependencies. Unit tests can be done easily. Using services that are injected, functionality typically is better separated. With this article you’ve seen that you do not need to invoke the
Dispose method of services yourself – this is dealt with from the container. You just need to create scopes to early dispose transient and scoped services.
If this information helped you with the architecture of your application or fixed issues, consider buying me multiple coffees. This helps staying up longer and writing more articles 🙂
The complete services lifetime sample is in the Professional C# 7 and .NET Core 2.0 repository.
My new book Professional C# 7 and .NET Core 2.0 has a complete chapter dedicated to dependency injection: chapter 20, Dependency Injection.
Enjoy learning and programming!