How Dependency Injection Is Managed in ASP.NET Core Internal Architecture
ASP.NET Core coordinates dependency injection through a centralized IServiceCollection/IServiceProvider pattern orchestrated by WebApplicationBuilder, which leverages an IServiceProviderFactory abstraction to construct the root service provider and manages scoped lifetimes per HTTP request.
The dotnet/aspnetcore repository implements dependency injection as a first-class infrastructure concern woven directly into the hosting layer. Understanding how dependency injection is managed in ASP.NET Core internal architecture reveals the extensibility points that allow the framework to support everything from internal singletons to custom third-party containers like Autofac or DryIoc.
The IServiceCollection and IServiceProvider Foundation
At the heart of the system lies the Microsoft.Extensions.DependencyInjection abstractions. The framework uses IServiceCollection as a mutable registry of service descriptors and IServiceProvider as the resolved container instance.
In src/DefaultBuilder/src/WebApplicationBuilder.cs, the WebApplicationBuilder initializes a default service collection during construction. User code interacts with this collection through the builder.Services property, calling extension methods like AddControllers() or AddAuthentication() to populate descriptors before the host is built.
Basic registration follows this pattern:
var builder = WebApplication.CreateBuilder(args);
// Register application services
builder.Services.AddSingleton<IMySingleton, MySingleton>();
builder.Services.AddScoped<IMyScoped, MyScoped>();
builder.Services.AddTransient<IMyTransient, MyTransient>();
var app = builder.Build();
// Resolve a service manually (usually via constructor injection)
var singleton = app.Services.GetRequiredService<IMySingleton>();
The Host Builder Architecture
WebApplicationBuilder as the Entry Point
The modern ASP.NET Core stack centers on WebApplicationBuilder, defined in src/DefaultBuilder/src/WebApplicationBuilder.cs. This builder aggregates service registrations and configuration into a cohesive host.
When Build() is invoked, the builder delegates to the generic host infrastructure to create the concrete service provider. The key abstraction enabling this flexibility is IServiceProviderFactory<TBuilder>.
Service Provider Factory Pattern
The host obtains an IServiceProviderFactory<IServiceCollection> to transform the collected service descriptors into a functioning IServiceProvider. By default, ASP.NET Core uses DefaultServiceProviderFactory, which ships in the Microsoft.Extensions.DependencyInjection package.
In src/DefaultBuilder/src/ConfigureHostBuilder.cs, the UseServiceProviderFactory<TBuilder> method allows developers to override this default. This is the extension point that enables third-party containers, as the custom factory receives the populated IServiceCollection and returns the root IServiceProvider stored in IHost.Services.
Request-Scoped Service Resolution
For each HTTP request, ASP.NET Core creates an IServiceScope to isolate scoped service instances. This ensures that services registered with ServiceLifetime.Scoped (like Entity Framework Core contexts) exist only for the duration of a single request.
When a request arrives at the server, the framework creates a scope from the root provider. Middleware, controllers, and Razor Pages resolve their dependencies from this scoped provider. When the response completes, the framework disposes the scope and all scoped services.
app.MapGet("/weather", (IWeatherService weather, IHttpContextAccessor ctx) =>
{
// `weather` is resolved from the request's scoped provider
return weather.GetForecast();
});
Component Registration via Extension Methods
Every ASP.NET Core subsystem registers its services through static extension methods following the Add{Feature} convention. These methods live in ServiceCollectionExtensions classes spread across the repository:
- MVC services register in
src/Mvc/Mvc/src/MvcServiceCollectionExtensions.cs, adding descriptors forIActionDescriptorCollectionProvider,IModelMetadataProvider, andIUrlHelperFactory. - Routing services register in
src/Http/Routing/src/DependencyInjection/RoutingServiceCollectionExtensions.cs. - Authentication services register in
src/Security/Authentication/Core/src/AuthenticationServiceCollectionExtensions.cs. - Health checks register in
src/HealthChecks/HealthChecks/src/DependencyInjection/HealthCheckServiceCollectionExtensions.cs.
Each extension method follows the same pattern:
builder.Services.AddControllers(); // registers MVC controllers, filters, model binders, etc.
builder.Services.AddRazorPages(); // registers Razor Pages services
builder.Services.AddMvcCore(); // low‑level MVC core services
All of the above call into MvcServiceCollectionExtensions.cs, which adds dozens of framework services to the same IServiceCollection.
Replacing the Default Container
Developers can replace the built-in DI container by implementing a custom IServiceProviderFactory<TBuilder>. In src/Hosting/Hosting/src/WebHostBuilder.cs and related host builder classes, the UseServiceProviderFactory extension stores the factory in the host builder properties. When Build() executes, the host retrieves this factory and calls CreateServiceProvider to produce the container.
Example with Autofac:
using Autofac.Extensions.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
// Replace the default provider
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
// Register Autofac modules or direct registrations
builder.Host.ConfigureContainer<ContainerBuilder>(container =>
{
container.RegisterModule(new MyAutofacModule());
});
var app = builder.Build();
Summary
IServiceCollectionacts as the mutable registry of service descriptors during startup.IServiceProviderFactory<IServiceCollection>(default:DefaultServiceProviderFactory) transforms the collection into a concreteIServiceProvider.WebApplicationBuilderinsrc/DefaultBuilder/src/WebApplicationBuilder.cscoordinates registration and defers provider creation to the factory.- Scoped services are isolated per HTTP request via
IServiceScopeinstances created by the framework. - Extension methods like
AddMvc()andAddAuthentication()modularize service registration across the repository. - Custom containers integrate via
UseServiceProviderFactory<TBuilder>, allowing complete replacement of the default DI implementation.
Frequently Asked Questions
What is the role of IServiceProviderFactory in ASP.NET Core?
The IServiceProviderFactory abstraction decouples the service collection from the concrete container implementation. It receives the populated IServiceCollection and returns an IServiceProvider, allowing the framework to support the default Microsoft container or third-party alternatives like Autofac without changing consumer code.
How does ASP.NET Core handle scoped services per request?
The framework creates a new IServiceScope from the root provider at the beginning of each HTTP request. All scoped service resolutions during that request share the same scope instance. When the response completes, the framework disposes the scope, triggering disposal of all scoped and transient services created within it.
Can I replace the built-in DI container with Autofac or DryIoc?
Yes. Call builder.Host.UseServiceProviderFactory() with an instance of your preferred container's provider factory (e.g., AutofacServiceProviderFactory). This replaces the DefaultServiceProviderFactory and allows the custom container to handle all service resolutions for the application lifetime.
Where are MVC services registered in the ASP.NET Core source?
MVC services register in src/Mvc/Mvc/src/MvcServiceCollectionExtensions.cs via the AddMvc(), AddControllers(), and AddRazorPages() extension methods. These methods add framework services like IActionDescriptorCollectionProvider, IModelMetadataProvider, and IUrlHelperFactory to the global IServiceCollection.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →