In last post, we discussed a bit about dependency inversion principle and how it is implemented in .NET. In this post, let’s discuss a bit more about service lifetimes and some other basic terms.
Dependency, Service and Client
There are three important words which we may use a lot while talking about dependency injections, viz, dependency, service and client.
dependency is any object that another object depends upon. So, if you have a
Repository class, which uses
SqlDataReader to read the data from database, then
SqlDataReader is a dependency for Repository class.
The object which receives dependency is called as
client, while the object which is passed is called as
service. In above example,
Repository is client and
SqlDataReqder is the service being passed to the client.
How does DI work ?
We already have seen what is a service in the context of dependency injection. These services are added and configured in
IServiceCollection implementation. The
IServiceProvider instance and it acts as a container.
This means, in any application, where you want to use the default dependency injection mechanism provided by .NET, you will have to create the IHost instance.
The IServiceProvider has responsibility to maintain the pool objects, create new if required or dispose the objects which are not required anymore. It takes the decisions based on the lifetimes configured in IServiceCollection.
As the name suggests, service lifetimes decide when a new object should be created and when an existing object should be used for injecting the dependencies. It also decides when/how the objects would be disposed. There are three different lifetimes,
Singleton. Let’s try to know a bit more about them.
Every time a transient dependency is requested, the container provides new instance of the dependency. This might be best suitable for stateless objects.
We can register transient services with AddTransient call.
This service lifetime is basically important for the web applications.
A scope basically starts when a client request is received by a web app and it ends when the request processing is completed.
We can register scoped services with AddScoped. The scoped objects are created when the client request is received and disposed when the request processing is complete.
A scoped service cannot be resolved by a Singleton service (either directly or indirectly).
Every time a singleton dependency is requested, the container provides same object. So if we register any dependency as Singleton, then it means there will be always 1 and only 1 object, which will be passed to all the clients requesting that object.
There are two ways in which Singleton objects are created:
- first time it is requested
- or the developer can decide to create the object and pass that object to the container. Practically, this is almost never required.
We can register singleton services with AddSingleton call. We should ensure that the Singleton services are thread safe. We should not implement singleton design pattern as container can take care of managing the lifetime of singleton object.
The singleton objects are disposed when the application is shutdown, because IServiceProvider is disposed only after application is shutdown.
Once the dependencies are registered in the container (which typically happens during application bootstrap), they can be used by other objects. For using the dependency, the client object can specify the dependencies in their constructors.
Constructors can also have mix arguments some can be resolved by dependency injection while others might not be provided by DI container. Such dependencies which cannot be provided dependency injection container, should have the default values specified.
Below code snippet from .NET Core MVC web application shows
ConfigureServices method registering dependencies and how those dependencies are used by
I hope this information is useful. Let me know your thoughts.