Dependency Injection
08 May 2020 | CSharp-ASP.NET CoreBuilt-In IoC Container
ASP.NET Core supports dependency injection(DI), which is a technique for achieving Inverse of Control(IoC). The Built-In IoC container doesn’t provide as many features as other third libraries. If you want to use other features, like property injection, you can use third libraries instead(more info).
The class(types) managed by IoC container is called service. IoC container will automatically inject services into our constructors or action methods.
Usually, project services have two types. There are:
- Framework Service: The service that ASP.NET Core defined, like
ILogger<T>
、IWebHostEnvironment
、IHostApplicationLifetime
. - Application Service: The service that you create for your application as a programmer.
Next! I will explain how to register services? What’s the service lifetime? How to retrieve service from the IoC container?
Where to register service?
By default, ASP.NET Core uses Startup
class as its startup type in Program.cs file. The specified startup class is used to register service and handle middleware.
Here we follow the ASP.NET Core default convention. The Startup
class has two methods that are ConfigureServices
and Configure
. The ConfigureServices
is used to register our services. The Configure
is used to set up middleware.
The ConfigureServices
has an IServiceCollection
parameter, which is an instance that can register application services on.
Consider the following example:
Log.cs
public interface ILog
{
void Write(string message);
}
public class ConsoleLog : ILog
{
public void Write(String message)
{
Console.Write(message);
}
}
Let us register the above instance into the IoC container.
Startup.cs
public class Startup{
public void ConfigureServices(IServiceCollection services){
services.Add(new ServiceDescriptor(typeof(ILog),typeof(ConsoleLog),ServiceLifetime.Singleton));
}
}
The ServiceDescriptor describes the service with its service type, implementation, and lifetime. Here, we specified the ILog
as a service and the ConsoleLog
as its implementation, and register it as a singleton lifetime.
Service lifetime
The IoC container keeps track of the lifetime of service and disposes of its resource when their life ends.
There are three types of service lifetime.
-
Transient: The IoC container will create a new instance each time when they are requested.
-
Scoped: The IoC container will create a new instance of the specified service type once per client request.
-
Singleton: The IoC container will create an instance and share it throughout the application lifetime. Every subsequent request gets the same as first.
There are many things to pay attention to when dealing with Dependency Injection. The following are some recommendations for better to built application.
- Register your service as a transient wherever possible, because you didn’t need to care a lot of multi-threading, and memory leak problems.
- Consider multi-threading when designing scoped service. Do not resolve scoped service from singleton service. It is dangerous.
- Use singleton carefully, because you need to deal with multi-threading and potential memory-leak problem.
Registration method
we can register our service through ServiceDescriber
, which provides a lot of overload constructors.
The following example illustrates various usages of the ServiceDescriber
constructor.
services.Add(new ServiceDescriptor(typeof(ILog),new ConsoleLog()));//singleton
services.Add(new ServiceDescriptor(typeof(ILog),typeof(ConsoleLog)));//singleton
services.Add(new ServiceDescriptor(typeof(ILog), typeof(ConsoleLog), ServiceLifetime.Transient));//transient
services.Add(new ServiceDescriptor(typeof(ILog),typeof(ConsoleLog),ServiceLifetime.Scoped));//scoped
Microsoft also provides extension methods for each type of lifetime.
services.AddTransient<ConsoleLog>();//transient
services.AddScoped<ConsoleLog>();//scoped
services.AddSingleton<ConsoleLog>();//singleton
Constructor Injection
Once we register services into the IoC container, the ASP.NET Core will automatically inject services into target constructor, when its parameters are service types.
Constructors can accept arguments that aren’t provided by dependency injection, but the arguments must assign default values.
public class HomeController : Controller
{
private readonly ILog _log;
public HomeController(ILog log)
{
_log = log;
}
public IActionResult Index()
{
_log.Write("hello, I come from console log");
return View();
}
}
The above simple example injects ILog
into the HomeController
constructor.
Switch debug profile to the current project name. because we are using a Console.Write()
to print debug information, which is not working with IIS.
As my project name is DependencyInjection
, so I switched to DependencyInjection profile.
Then start to debug your project, open https://localhost:5001/ or http://localhost:5000/
Once you open it in a browser. View your console window.
Action Method Injection
ASP.NET Core provides an action method injection, which injects the service type parameter into an action method.
The FromServicesAttribute
specifies that an action parameter should be bound using the request services.
public class HomeController : Controller
{
public IActionResult Index([FromServices] ILog _log)
{
_log.Write("hello, I come from console log");
return View();
}
}
Comments