Understanding built-in IoC container
12 May 2020 | CSharp-ASP.NET CoreIn this article, we are going dive into the built-in IoC container. In the previous article, we have the following code.
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ConsoleLog>();//transient
}
As you have already known that the IServiceCollection
is the place for register services. But, where is the place to retrieve services? The answer is IServiceProvider
.
There have two types of assemblies, which are Microsoft.Extensions.DependencyInjection.Abstractions
and Microsoft.Extensions.DependencyInjection
. The previous one define interfaces, and another implement that. The following is a diagram illustrating the working mechanism.
As we can see from the above diagram, the ServiceProvider
(link) depends on the IEnumerator<ServiceDescriptor>
, which generated by the GetEnumerator
method in ServiceCollection
(link), where is the place registers our service.
The IServiceProviderEngine
instance implements the logic behind it in ServiceProvider
, and the IServiceProviderEngine
has a lot of implementation classes. Each implementation has a different performance.
Now, we understand the working mechanism. Put our services into IServiceCollection, retrieve them back from IServiceProvider. As the ServiceCollection
constructors are public, so it’s easy for us to get its instance. We can new a ServiceCollection instance, or directly use it in ConfigureServices()
in the ASP.NET Core application.
Startup.cs
public class Startup{
public void ConfigureServices(IServiceCollection services){
//some operations
}
}
The ServiceProvider constructor is an internal access modifier, so we can’t instance it. So, how can we get the ServiceProvider instance?
We can’t directly new a ServiceProvider instance. But, Microsoft provides many extension methods to retrieve the ServiceProvider instance. The following lists some ways for that.
get IServiceProvider through IServiceCollection
public class Startup{
public void ConfigureServices(IServiceCollection services)
{
IServiceProvider provider = services.BuildServiceProvider();
//T service = provider.getService<T>();
}
}
get IServiceProvider through HttpContext
public class CustomController : Controller
{
public IActionResult Index()
{
IServiceProvider provider = HttpContext.RequestServices;
//T service = provider.getService<T>();
return View();
}
}
get IServiceProvider through IApplicationBuilder
public class Startup{
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
IServiceProvider provider = app.ApplicationServices;
//T service = provider.getService<T>();
}
}
get IServiceProvider through IHost
public class Program
{
public static void Main(string[] args)
{
IHost host = CreateHostBuilder(args).Build();
IServiceProvider provider = host.Services;
//T service = provider.getService<T>();
host.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Because we can get IServiceProvider from IServiceCollection, and IServiceCollection has a public constructor. So, built-in DI also can be used in non-ASP applications. Here is an example that illustrates how to use built-in DI in a .NET Core Console application.
Comments