Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Note
This isn't the latest version of this article. For the current release, see the .NET 10 version of this article.
Warning
This version of ASP.NET Core is no longer supported. For more information, see the .NET and .NET Core Support Policy. For the current release, see the .NET 10 version of this article.
By Kirk Larkin, Steve Smith, and Brandon Dahler
ASP.NET Core supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies.
This article provides information on DI in ASP.NET Core web apps. For information on DI in all types of apps, including apps other than web apps, see Dependency injection in .NET.
Guidance that adds to or supersedes the guidance in this article is found the following articles:
- ASP.NET Core Blazor dependency injection
- Dependency injection into controllers in ASP.NET Core
- Use DI services to configure options
Code examples in this article are based on Blazor. To see Razor Pages examples, see the 7.0 version of this article.
View or download sample code (how to download)
When using the sample code in this article in a local Blazor Web App for demonstration purposes, adopt an interactive render mode.
Overview of dependency injection
A dependency is an object that another object depends on. Consider the following MyDependency class with a WriteMessage method:
public class MyDependency
{
public void WriteMessage(string message)
{
Console.WriteLine($"MyDependency.WriteMessage: {message}");
}
}
A class can create an instance of the MyDependency class to call its WriteMessage method. In the following example, the MyDependency class is a dependency of a Razor component.
Pages/DependencyExample1.razor:
@page "/dependency-example-1"
<button @onclick="WriteMessage">Write message</button>
@code {
private readonly MyDependency dependency = new MyDependency();
private void WriteMessage() =>
dependency.WriteMessage("DependencyExample1.WriteMessage called");
}
A class can create an instance of the MyDependency class to call its WriteMessage method. In the following example, the MyDependency class is a dependency of an IndexModel page class.
Pages/Index.cshtml.cs:
public class IndexModel : PageModel
{
private readonly MyDependency _dependency = new MyDependency();
public void OnGet()
{
_dependency.WriteMessage("IndexModel.OnGet called");
}
}
The consuming class creates and directly depends on the MyDependency class. Taking a direct dependency, such as in the previous example, is problematic and should be avoided for the following reasons:
- To replace
MyDependencywith a different implementation, the consuming class must be modified. - If
MyDependencyhas dependencies, they must also be configured by the consuming class. In a large project with multiple classes depending onMyDependency, the configuration code becomes scattered around the app. - The implementation is difficult to unit test.
DI addresses these problems through:
- The use of an interface or base class to abstract the dependency implementation.
- Registration of the dependency in a service container, also called a DI container. ASP.NET Core provides a built-in service container, IServiceProvider. Services are typically registered in the app's
Programfile (.NET 6 or later) or the app'sStartupfile (.NET 5 or earlier). - Injection of the service into classes where it's used. The framework creates instances of dependencies and disposes of them when they're no longer required.
In the following example, the IMyDependency interface defines the WriteMessage method signature.
Interfaces/IMyDependency.cs:
public interface IMyDependency
{
void WriteMessage(string message);
}
The preceding interface is implemented by the following concrete type, MyDependency.
Services/MyDependency.cs:
public class MyDependency : IMyDependency
{
public void WriteMessage(string message)
{
Console.WriteLine($"MyDependency.WriteMessage: {message}");
}
}
The app registers the IMyDependency service with the concrete type MyDependency where services are added to the service container, usually either in the Program file (.NET 6 or later) or Startup.ConfigureServices method (.NET 5 or earlier). The AddScoped method registers the service with a scoped lifetime, which is the lifetime of a Blazor circuit (.NET 8 or later) or a single request in an MVC or Razor Pages app. Service lifetimes are described later in this article.
builder.Services.AddScoped<IMyDependency, MyDependency>();
services.AddScoped<IMyDependency, MyDependency>();
The IMyDependency service is requested and used to call the WriteMessage method, as the following Razor component demonstrates.
Pages/DependencyExample2.razor:
@page "/dependency-example-2"
@inject IMyDependency Dependency
<button @onclick="WriteMessage">Write message</button>
@code {
private void WriteMessage() =>
Dependency.WriteMessage("DependencyExample2.WriteMessage called");
}
The IMyDependency service is requested and used to call the WriteMessage method, as the following page model class demonstrates.
Pages/Index.cshtml.cs:
public class IndexModel(IMyDependency dependency) : PageModel
{
public void OnGet()
{
dependency.WriteMessage("IndexModel.OnGet called");
}
}
By using the DI pattern, the class that consumes the dependency:
- Doesn't use the concrete type
MyDependency, only theIMyDependencyinterface it implements. That makes it easy to change the implementation without modifying the consumer. - Doesn't directly create an instance of
MyDependencyor dispose of it. The dependency is created and disposed by the service container.
The IMyDependency interface implementation can be improved by using the built-in logging API, which is injected as a dependency in the following example.
Services/MyDependency.cs:
public class MyDependency(ILogger<MyDependency> logger) : IMyDependency
{
public void WriteMessage(string message)
{
logger.LogInformation($"MyDependency.WriteMessage: {message}");
}
}
MyDependency depends on ILogger<TCategoryName>, a framework-provided service.
It's common to use DI in a chained fashion. Each requested dependency in turn requests its own dependencies. The container resolves the dependencies in the graph and returns the fully resolved service. The collective set of dependencies that must be resolved is typically referred to as a dependency tree, dependency graph, or object graph.
The container resolves ILogger<TCategoryName> by taking advantage of (generic) open types, eliminating the need to register every (generic) constructed type.
In DI terminology, a service:
- Is typically an object that provides a service to other objects, such as the preceding
IMyDependencyservice. - Isn't related to a web service, although the service might use a web service.
The IMyDependency implementations shown in the preceding examples were written to demonstrate general DI principles, not to implement logging. Most apps shouldn't need to create loggers, as the preceding examples show. The following code demonstrates directly using the framework's built-in logging API, which doesn't require the registration of a custom service (IMyDependency).
Pages/LoggingExample.razor:
@page "/logging-example"
@inject ILogger<LoggingExample> Logger
<button @onclick="WriteMessage">Write message</button>
@code {
private void WriteMessage() =>
Logger.LogInformation("LoggingExample.WriteMessage called");
}
Pages/IndexModel.cshtml.cs:
public class IndexModel(ILogger<IndexModel> logger) : PageModel
{
public void OnGet()
{
logger.LogInformation("IndexModel.OnGet called");
}
}
Services injected into Startup
Services can be injected into the Startup constructor and the Startup.Configure method.
The Generic Host (IHostBuilder) of ASP.NET Core 3.0 or later, uses a single service container throughout the app's lifecycle after a temporary "root" service provider uses essential services for the host to start and configure the app's main service container. Most services, including custom services and framework services not involved in host startup, aren't configured or available in the service container when the Startup constructor is called. Only the following services can be injected into the Startup constructor when using the Generic Host:
By restricting the available services available in the Startup class constructor, the Generic Host prevents you from trying to use a service before it's created or available and from creating multiple instances of custom or framework singleton services, where a singleton service created in the temporary service container could be different from one created in the final service container.
Any service registered with the service container can be injected into the Startup.Configure method. In the following example, an ILogger<TCategoryName> is injected:
public void Configure(IApplicationBuilder app, ILogger<Startup> logger)
{
...
}
For more information, see App startup in ASP.NET Core and Access configuration in Startup.
Service registration methods
For general guidance on service registrations, see Service registration.
It's common to use multiple implementations when mocking types for testing. For more information, see Integration tests in ASP.NET Core.
Registering a service with only an implementation type is equivalent to registering the service with the same implementation and service type:
builder.Services.AddSingleton<MyDependency>();
services.AddSingleton<MyDependency>();
Service registration methods can be used to register multiple service instances of the same service type. In the following example, AddSingleton is called twice with IMyDependency as the service type. The second call to AddSingleton overrides the previous one when resolved as IMyDependency and adds to the previous one when multiple services are resolved via IEnumerable<IMyDependency>.
builder.Services.AddSingleton<IMyDependency, MyDependency>();
builder.Services.AddSingleton<IMyDependency, DifferentDependency>();
public class MyService
{
public MyService(IMyDependency myDependency,
IEnumerable<IMyDependency> myDependencies)
{
Trace.Assert(myDependency is DifferentDependency);
var dependencyArray = myDependencies.ToArray();
Trace.Assert(dependencyArray[0] is MyDependency);
Trace.Assert(dependencyArray[1] is DifferentDependency);
}
}
services.AddSingleton<IMyDependency, MyDependency>();
services.AddSingleton<IMyDependency, DifferentDependency>();
public class MyService
{
public MyService(IMyDependency myDependency,
IEnumerable<IMyDependency> myDependencies)
{
Trace.Assert(myDependency is DifferentDependency);
var dependencyArray = myDependencies.ToArray();
Trace.Assert(dependencyArray[0] is MyDependency);
Trace.Assert(dependencyArray[1] is DifferentDependency);
}
}
Register groups of services with extension methods
The ASP.NET Core framework convention for registering a group of related services is to use a single Add{GROUP NAME} extension method to register all of the services required by a framework feature, where the {GROUP NAME} placeholder is a descriptive group name. For example, the AddRazorComponents extension method registers services required for server-side rendering of Razor components.
Consider the following example that configures options and registers services:
builder.Services.Configure<PositionOptions>(
builder.Configuration.GetSection(PositionOptions.Position));
builder.Services.Configure<ColorOptions>(
builder.Configuration.GetSection(ColorOptions.Color));
builder.Services.AddScoped<IMyDependency, MyDependency>();
builder.Services.AddScoped<IMyDependency2, MyDependency2>();
services.Configure<PositionOptions>(
builder.Configuration.GetSection(PositionOptions.Position));
services.Configure<ColorOptions>(
builder.Configuration.GetSection(ColorOptions.Color));
services.AddScoped<IMyDependency, MyDependency>();
services.AddScoped<IMyDependency2, MyDependency2>();
Related groups of registrations can be moved to an extension method to register services. In the following example:
- The
AddConfigextension method binds configuration data to strongly-typed C# classes and registers the classes in the service container. - The
AddDependencyGroupextension method adds additional class (service) dependencies.
namespace Microsoft.Extensions.DependencyInjection;
public static class ConfigServiceCollectionExtensions
{
public static IServiceCollection AddConfig(
this IServiceCollection services, IConfiguration config)
{
services.Configure<PositionOptions>(
config.GetSection(PositionOptions.Position));
services.Configure<ColorOptions>(
config.GetSection(ColorOptions.Color));
return services;
}
public static IServiceCollection AddDependencyGroup(
this IServiceCollection services)
{
services.AddScoped<IMyDependency, MyDependency>();
services.AddScoped<IMyDependency2, MyDependency2>();
return services;
}
}
The following code calls the preceding AddConfig and AddDependencyGroup extension methods to register the services:
builder.Services
.AddConfig(builder.Configuration)
.AddDependencyGroup();
services
.AddConfig(builder.Configuration)
.AddDependencyGroup();
We recommend that apps follow the naming convention of creating extension methods in the Microsoft.Extensions.DependencyInjection namespace, which:
- Encapsulates groups of service registrations.
- Provides convenient IntelliSense access to the service.
Service lifetimes
For general guidance on service lifetimes, see Service lifetimes. For additional service lifetime guidance that applies to Blazor apps, see ASP.NET Core Blazor dependency injection.
To use scoped services in middleware, use one of the following approaches:
- Inject the service into the middleware's
InvokeorInvokeAsyncmethod. Using constructor injection throws a runtime exception because it forces the scoped service to behave like a singleton. The sample in the Lifetime and registration options section demonstrates theInvokeAsyncapproach. - Use factory-based middleware. Middleware registered using this approach is activated per client request (connection), which allows scoped services to be injected into the middleware's constructor.
For more information, see the following resources:
- Write custom ASP.NET Core middleware
- For more information on using keyed services with Razor components, see ASP.NET Core Blazor dependency injection.
Keyed services
Keyed services registers and retrieves services using keys. A service is associated with a key by calling any of the following extension methods for service registration:
The following example demonstrates keyed services with the following API:
IStringCacheis the service interface with aGetmethod signature.StringCache1andStringCache2are concrete service implementations forIStringCache.
Interfaces/IStringCache.cs:
public interface IStringCache
{
string Get(int key);
}
Services/StringCache1.cs:
public class StringCache1 : IStringCache
{
public string Get(int key) => $"Resolving {key} from StringCache1.";
}
Services/StringCache2.cs:
public class StringCache2 : IStringCache
{
public string Get(int key) => $"Resolving {key} from StringCache2.";
}
Access a registered service by specifying the key with the [FromKeyedServices] attribute.
Keyed service and example Minimal API endpoints in the Program file:
builder.Services.AddKeyedSingleton<IStringCache, StringCache1>("cache1");
builder.Services.AddKeyedSingleton<IStringCache, StringCache2>("cache2");
...
app.MapGet("/cache1", ([FromKeyedServices("cache1")] IStringCache stringCache1) =>
stringCache1.Get(1));
app.MapGet("/cache2", ([FromKeyedServices("cache2")] IStringCache stringCache2) =>
stringCache2.Get(2));
Example use in a Razor componment (Pages/KeyedServicesExample.razor) using the [Inject] attribute. Use the InjectAttribute.Key property to specify the key for the service to inject:
@page "/keyed-services-example"
@Cache?.Get(3)
@code {
[Inject(Key = "cache1")]
public IStringCache? Cache { get; set; }
}
For more information on using keyed services with Razor components, see ASP.NET Core Blazor dependency injection.
Example use in a SignalR hub (Hubs/MyHub1.cs) with primary constructor injection:
using Microsoft.AspNetCore.SignalR;
public class MyHub1([FromKeyedServices("cache2")] IStringCache cache) : Hub
{
public void Method()
{
Console.WriteLine(cache.Get(4));
}
}
Example use in a SignalR hub (Hubs/MyHub2.cs) with method injection:
using Microsoft.AspNetCore.SignalR;
public class MyHub2 : Hub
{
public void Method([FromKeyedServices("cache2")] IStringCache cache)
{
Console.WriteLine(cache.Get(5));
}
}
Middleware supports keyed services in both the middleware's constructor and its Invoke/InvokeAsync method:
internal class MyMiddleware
{
private readonly RequestDelegate _next;
public MyMiddleware(RequestDelegate next,
[FromKeyedServices("cache1")] IStringCache cache)
{
_next = next;
Console.WriteLine(cache.Get(6));
}
public Task Invoke(HttpContext context,
[FromKeyedServices("cache2")] IStringCache cache)
{
Console.WriteLine(cache.Get(7));
return _next(context);
}
}
In the app processing pipeline of the Program file (.NET 6 or later) or the Startup.Configure method (.NET 5 or earlier):
app.UseMiddleware<MyMiddleware>();
For more information on creating middleware, see Write custom ASP.NET Core middleware.
Constructor injection behavior
For more information on constructor injection behavior, see the following resources:
Entity Framework contexts
For guidance on EF Core in server-side Blazor apps, see ASP.NET Core Blazor with Entity Framework Core (EF Core).
By default, Entity Framework contexts are added to the service container using the scoped lifetime because web app database operations are normally scoped to the client request. To use a different lifetime, specify the lifetime by using an AddDbContext overload. Services of a given lifetime shouldn't use a database context with a lifetime that's shorter than the service's lifetime.
Lifetime and registration options
To demonstrate the difference between service lifetimes and their registration options, consider the following interfaces that represent a task as an operation with an identifier, OperationId. Depending on how the lifetime of an operation's service is configured for the following interfaces, the container provides either the same or different instances of the service when requested by a class.
IOperation.cs:
public interface IOperation
{
string OperationId { get; }
}
public interface IOperationTransient : IOperation { }
public interface IOperationScoped : IOperation { }
public interface IOperationSingleton : IOperation { }
The following Operation class implements all of the preceding interfaces. The Operation constructor generates a GUID and stores the last four characters in the OperationId property.
Operation.cs:
public class Operation : IOperationTransient, IOperationScoped, IOperationSingleton
{
public Operation()
{
OperationId = Guid.NewGuid().ToString()[^4..];
}
public string OperationId { get; }
}
The following code creates multiple registrations of the Operation class according to the named lifetimes.
Where services are registered:
builder.Services.AddTransient<IOperationTransient, Operation>();
builder.Services.AddScoped<IOperationScoped, Operation>();
builder.Services.AddSingleton<IOperationSingleton, Operation>();
services.AddTransient<IOperationTransient, Operation>();
services.AddScoped<IOperationScoped, Operation>();
services.AddSingleton<IOperationSingleton, Operation>();
The following example demonstrates object lifetimes both within and between requests. The Operation component and the middleware request each kind of IOperation type and log the OperationId for each.
Pages/OperationExample.razor:
@page "/operation-example"
@inject IOperationTransient TransientOperation
@inject IOperationScoped ScopedOperation
@inject IOperationSingleton SingletonOperation
<ul>
<li>Transient: @TransientOperation.OperationId</li>
<li>Scoped: @ScopedOperation.OperationId</li>
<li>Singleton: @SingletonOperation.OperationId</li>
</ul>
The following example demonstrates object lifetimes both within and between requests. The IndexModel and the middleware request each kind of IOperation type and log the OperationId for each.
IndexModel.cshtml.cs:
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
private readonly IOperationTransient _transientOperation;
private readonly IOperationScoped _scopedOperation;
private readonly IOperationSingleton _singletonOperation;
public IndexModel(ILogger<IndexModel> logger,
IOperationTransient transientOperation,
IOperationScoped scopedOperation,
IOperationSingleton singletonOperation)
{
_logger = logger;
_transientOperation = transientOperation;
_scopedOperation = scopedOperation;
_singletonOperation = singletonOperation;
}
public void OnGet()
{
_logger.LogInformation($"Transient: {_transientOperation.OperationId}");
_logger.LogInformation($"Scoped: {_scopedOperation.OperationId}");
_logger.LogInformation($"Singleton: {_singletonOperation.OperationId}");
}
}
Middleware can also resolve and use the same services. Scoped and transient services must be resolved in the InvokeAsync method.
MyMiddleware.cs:
public class MyMiddleware(ILogger<IndexModel> logger,
IOperationSingleton singletonOperation)
{
public async Task InvokeAsync(HttpContext context,
IOperationTransient transientOperation, IOperationScoped scopedOperation)
{
logger.LogInformation($"Transient: {transientOperation.OperationId}");
logger.LogInformation($"Scoped: {scopedOperation.OperationId}");
logger.LogInformation($"Singleton: {singletonOperation.OperationId}");
await _next(context);
}
}
public class MyMiddleware
{
private readonly ILogger<IndexModel> _logger;
private readonly IOperationSingleton _singletonOperation;
public MyMiddleware(ILogger<IndexModel> logger,
IOperationSingleton singletonOperation)
{
_logger = logger;
_singletonOperation = singletonOperation;
}
public async Task InvokeAsync(HttpContext context,
IOperationTransient transientOperation, IOperationScoped scopedOperation)
{
_logger.LogInformation($"Transient: {transientOperation.OperationId}");
_logger.LogInformation($"Scoped: {scopedOperation.OperationId}");
_logger.LogInformation($"Singleton: {_singletonOperation.OperationId}");
await _next(context);
}
}
In the app processing pipeline of the Program file (.NET 6 or later) or the Startup.Configure method (.NET 5 or earlier):
app.UseMiddleware<MyMiddleware>();
For more information on creating middleware, see Write custom ASP.NET Core middleware.
Output from the preceding examples shows:
- Transient objects are always different. The transient
OperationIdvalue is different for the Razor component and in the middleware. - Scoped objects are the same for a given request but differ across new Blazor circuits.
- Singleton objects are the same for every request or Blazor circuit.
- Transient objects are always different. The transient
OperationIdvalue is different for the page and in the middleware. - Scoped objects are the same for a given request but differ across new requests.
- Singleton objects are the same for every request.
Resolve a service at app startup
The following code shows how to resolve a scoped service for a limited duration when the app starts:
var app = builder.Build();
using (var serviceScope = app.Services.CreateScope())
{
var services = serviceScope.ServiceProvider;
var dependency = services.GetRequiredService<IMyDependency>();
dependency.WriteMessage("Call services from main");
}
Scope validation
For guidance on scope validation, see the following resources:
Request Services
Services and their dependencies within an ASP.NET Core request are exposed through HttpContext.RequestServices.
The framework creates a scope per request, and RequestServices exposes the scoped service provider. All scoped services are valid for as long as the request is active.
Note
Prefer requesting dependencies as constructor parameters over resolving services from RequestServices. Requesting dependencies as constructor parameters yields classes that are easier to test.
Design services for dependency injection
When designing services for DI:
- Avoid stateful, static classes and members. Avoid creating global state by designing apps to use singleton services instead.
- Avoid direct instantiation of dependent classes within services. Direct instantiation couples the code to a particular implementation.
- Make services small, well-factored, and easily tested.
If a class has many injected dependencies, it might be a sign that the class has too many responsibilities and violates the Single Responsibility Principle (SRP). Attempt to refactor the class by moving some of its responsibilities into new classes. Keep in mind that Razor Pages page model classes and MVC controller classes should focus on UI concerns.
Disposal of services
The container calls Dispose for the IDisposable types it creates. Services resolved from the container should never be disposed by the developer. If a type or factory is registered as a singleton, the container disposes the singleton automatically.
In the following example, the services are created by the service container and disposed automatically.
Services/Service1.cs:
public class Service1 : IDisposable
{
private bool _disposed;
public void Write(string message)
{
Console.WriteLine($"Service1: {message}");
}
public void Dispose()
{
if (_disposed)
{
return;
}
Console.WriteLine("Service1.Dispose");
_disposed = true;
GC.SuppressFinalize(this);
}
}
Services/Service2.cs:
public class Service2 : IDisposable
{
private bool _disposed;
public void Write(string message)
{
Console.WriteLine($"Service2: {message}");
}
public void Dispose()
{
if (_disposed)
{
return;
}
Console.WriteLine("Service2.Dispose");
_disposed = true;
GC.SuppressFinalize(this);
}
}
Services/Service3.cs:
public interface IService3
{
public void Write(string message);
}
public class Service3(string myKey) : IService3, IDisposable
{
private bool _disposed;
public void Write(string message)
{
Console.WriteLine($"Service3: {message}, Key = {myKey}");
}
public void Dispose()
{
if (_disposed)
{
return;
}
Console.WriteLine("Service3.Dispose");
_disposed = true;
GC.SuppressFinalize(this);
}
}
In appsettings.Development.json:
"Key": "Value from appsettings.Development.json"
Where services are registered by the app:
builder.Services.AddScoped<Service1>();
builder.Services.AddSingleton<Service2>();
var key = builder.Configuration["Key"] ?? string.Empty;
builder.Services.AddSingleton<IService3>(sp => new Service3(key));
services.AddScoped<Service1>();
services.AddSingleton<Service2>();
var myKey = builder.Configuration["Key"] ?? string.Empty;
services.AddSingleton<IService3>(sp => new Service3(myKey));
Pages/DisposalExample.razor:
@page "/disposal-example"
@inject Service1 Service1
@inject Service2 Service2
@inject IService3 Service3
@code {
protected override void OnInitialized()
{
Service1.Write("DisposalExample.OnInitialized");
Service2.Write("DisposalExample.OnInitialized");
Service3.Write("DisposalExample.OnInitialized");
}
}
The debug console shows the following output after each refresh of the Index page:
Service1: DisposalExample.OnInitialized
Service2: DisposalExample.OnInitialized
Service3: DisposalExample.OnInitialized, Key = Value from appsettings.Development.json
Service1.Dispose
To see the entry for the disposal of Service1, navigate away from the DisposalExample component to trigger its disposal.
Pages/Index.cshtml.cs:
public class IndexModel(
Service1 service1, Service2 service2, IService3 service3)
: PageModel
{
public void OnGet()
{
service1.Write("IndexModel.OnGet");
service2.Write("IndexModel.OnGet");
service3.Write("IndexModel.OnGet");
}
}
The debug console shows the following output after each refresh of the Index page:
Service1: IndexModel.OnGet
Service2: IndexModel.OnGet
Service3: IndexModel.OnGet, Key = Value from appsettings.Development.json
Service1.Dispose
Services not created by the service container
Consider the following code:
builder.Services.AddSingleton(new Service1());
builder.Services.AddSingleton(new Service2());
services.AddSingleton(new Service1());
services.AddSingleton(new Service2());
For the preceding code:
- The service instances aren't created by the service container.
- The framework doesn't dispose of the services automatically.
- The developer is responsible for disposing of the services.
IDisposable guidance for transient and shared instances
For more information, see Dependency injection in .NET: IDisposable guidance for transient and shared instance.
Default service container replacement
For more information, see Dependency injection in .NET: Default service container replacement.
Recommendations
For more information, see Dependency injection guidelines: Recommendations.
Avoid using the service locator pattern. For example, don't invoke GetService to obtain a service instance when you can use DI instead:
Incorrect:

Correct:
public class MyClass(IOptionsMonitor<MyOptions> optionsMonitor)
{
public void MyMethod()
{
var option = optionsMonitor.CurrentValue.Option;
...
}
}
Another service locator variation to avoid is injecting a factory that resolves dependencies at runtime. Both of these practices mix Inversion of Control strategies.
Avoid static access to HttpContext (for example, IHttpContextAccessor.HttpContext).
DI is an alternative to static/global object access patterns. You might not be able to realize the benefits of DI if you mix it with static object access.
Recommended patterns for multitenancy in dependency injection
Orchard Core is an app framework for building modular, multitenant apps on ASP.NET Core. For more information, see the Orchard Core Documentation.
For examples of how to build modular and multitenant apps using just the Orchard Core Framework without its CMS-specific features, see the Orchard Core samples.
Framework-provided services
The Program file (.NET 6 or later) or the Startup file (.NET 5 or earlier) registers services that the app uses, including platform features, such as Entity Framework Core and services to support Razor components in Blazor (.NET 8 or later). Initially, the IServiceCollection has services defined by the framework depending on how the host was configured. For apps based on the ASP.NET Core templates, the framework registers more than 250 services.
The following table describes a small sample of framework-registered services:
| Service type | Lifetime |
|---|---|
| Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory | Transient |
| IHostApplicationLifetime | Singleton |
| IWebHostEnvironment | Singleton |
| Microsoft.AspNetCore.Hosting.IStartup | Singleton |
| Microsoft.AspNetCore.Hosting.IStartupFilter | Transient |
| Microsoft.AspNetCore.Hosting.Server.IServer | Singleton |
| Microsoft.AspNetCore.Http.IHttpContextFactory | Transient |
| Microsoft.Extensions.Logging.ILogger<TCategoryName> | Singleton |
| Microsoft.Extensions.Logging.ILoggerFactory | Singleton |
| Microsoft.Extensions.ObjectPool.ObjectPoolProvider | Singleton |
| Microsoft.Extensions.Options.IConfigureOptions<TOptions> | Transient |
| Microsoft.Extensions.Options.IOptions<TOptions> | Singleton |
| System.Diagnostics.DiagnosticSource | Singleton |
| System.Diagnostics.DiagnosticListener | Singleton |
Additional resources
- ASP.NET Core Blazor dependency injection
- Dependency injection into views in ASP.NET Core
- Dependency injection into controllers in ASP.NET Core
- Dependency injection in requirement handlers in ASP.NET Core
- NDC Conference Patterns for DI app development
- App startup in ASP.NET Core
- Factory-based middleware activation in ASP.NET Core
- Understand dependency injection basics in .NET
- Dependency injection guidelines
- Tutorial: Use dependency injection in .NET
- .NET dependency injection
- ASP.NET Core Dependency Injection: What is the IServiceCollection?
- Four ways to dispose IDisposables in ASP.NET Core
- Writing Clean Code in ASP.NET Core with Dependency Injection (MSDN)
- Explicit Dependencies Principle
- Inversion of Control Containers and the Dependency Injection Pattern (Martin Fowler)
- How to register a service with multiple interfaces in ASP.NET Core DI (Andrew Lock)
- Avoiding Startup service injection in ASP.NET Core 3 (Andrew Lock)
ASP.NET Core