Dependency Injection - The last but not least
Cristian Lopes
Senior .NET | Xamarin & MAUI Developer | React/NextJs & React Native Enthusiast | Transforming Mobile and Web Experiences
Dependency Injection is the last principle from the acronym SOLID.
I don't know, but I believe that Dependency Injection is one of the most powerful principles in software development.
With Dependency Injection, we can test our apps without caring about actual implementations, making our software cleaner, safer, and more flexible.
With it, we can quickly create multi-tenancy apps. However, what I bring today is basic but essential, and sometimes we need help understanding the differences.
In a nutshell, Dependency injection works better together with the principle IoC(Inversion of Control).
With Ioc, we usually will see the dependencies on the constructor of our classes. In this case, it will be necessary to provide the implementation when we want to create this object.
Service Provider
And finally, we have our service provider to make this easier for us.
The service provider will store every configuration that you add to ServiceCollection and provides it to our application every time we asked for.
How could we add something to this collection?
builder.Services.AddTransient<ITransientService, TransientService>();
builder.Services.AddScoped<IScopedService, ScopedService>();? ?
builder.Services.AddSingleton<ISingletonService, SingletonService>();
Remember to register services before the application is built to add the services to the ServiceCollection correctly.
In .net, we have three ways that we can register one dependency. Transient, Scoped, and Singleton.
I created a simple web API for this sample to inject these three services.
? ? public interface IServiceBase
? ? {
? ? ? ? DateTime CreatedAt { get; set; }
? ? ? ? Guid Id { get; set; }
? ? }
? ? public class ServiceBase : IServiceBase
? ? {
? ? ? ? public Guid Id { get; set; } = Guid.NewGuid();
? ? ? ? public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
? ? }
? ? public interface IScopedService : IServiceBase { }
? ? public class ScopedService : ServiceBase, IScopedService
? ? {
? ? }
? ? public interface ITransientService : IServiceBase { }
? ? public class TransientService : ServiceBase, ITransientService
? ? {
? ? }
? ? public interface ISingletonService : IServiceBase { }
? ? public class SingletonService : ServiceBase, ISingletonService
? ? {
? ? }
And here are the injection.
builder.Services.AddTransient<ITransientService, TransientService>();
builder.Services.AddScoped<IScopedService, ScopedService>();
builder.Services.AddSingleton<ISingletonService, SingletonService>();
And the HomeController has these simple codes to understand the differences.
领英推荐
? ? [ApiController]
? ? [Route("[controller]")]
? ? public class HomeController : ControllerBase
? ? {
? ? ? ? private readonly IServiceProvider _serviceProvider;
? ? ? ? public HomeController(IServiceProvider serviceProvider)
? ? ? ? {
? ? ? ? ? ? _serviceProvider = serviceProvider;
? ? ? ? }
? ? ? ? [HttpGet("GetTransient")]
? ? ? ? public IEnumerable<IServiceBase> GetTransient()
? ? ? ? {
? ? ? ? ? ? var list = new List<IServiceBase>();
? ? ? ? ? ? using (var serviceScope = _serviceProvider.CreateScope())
? ? ? ? ? ? {
? ? ? ? ? ? ? ? for (int i = 0; i < 5; i++)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? list.Add(serviceScope.ServiceProvider.GetService<ITransientService>());
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? return list;
? ? ? ? }
? ? ? ? [HttpGet("GetScoped")]
? ? ? ? public IEnumerable<IServiceBase> GetScoped()
? ? ? ? {
? ? ? ? ? ? var list = new List<IServiceBase>();
? ? ? ? ? ? using (var serviceScope = _serviceProvider.CreateScope())
? ? ? ? ? ? {
? ? ? ? ? ? ? ? for (int i = 0; i < 5; i++)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? list.Add(serviceScope.ServiceProvider.GetService<IScopedService>());
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? return list;
? ? ? ? }
? ? ? ? [HttpGet("GetSingleton")]
? ? ? ? public IEnumerable<IServiceBase> GetSingleton()
? ? ? ? {
? ? ? ? ? ? var list = new List<IServiceBase>();
? ? ? ? ? ? using (var serviceScope = _serviceProvider.CreateScope())
? ? ? ? ? ? {
? ? ? ? ? ? ? ? for (int i = 0; i < 5; i++)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? list.Add(serviceScope.ServiceProvider.GetService<ISingletonService>());
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? return list;
? ? ? ? }
? ? }
?Transient
Dependency injected as a Transient will always create a new implementation.
Scoped
Dependency injected as a Scoped will create a new implementation for scope.
It is a web API application. Then we have a different scope for requests. We can see that when we call the method again, the service provider creates a new implementation.
Singleton
Last but not least.
Singleton is created once when the application is built, and we will have the same instance in every place we call them.
This is as easy as it seems.
This post can help someone.
And please USE the dependency correctly. Injecting everything that the class needs to exist on the constructor does not do, like me injecting the provider. This can introduce issues on runtime and become harder to create tests.
That's it.
I hope to write to you all soon.
References