Movatterモバイル変換


[0]ホーム

URL:


Skip to main content

This browser is no longer supported.

Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.

Download Microsoft EdgeMore info about Internet Explorer and Microsoft Edge
Table of contentsExit editor mode

Dependency injection in ASP.NET Core

Feedback

In this article

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 9 version of this article.

ByKirk Larkin,Steve Smith, andBrandon Dahler

ASP.NET Core supports the dependency injection (DI) software design pattern, which is a technique for achievingInversion of Control (IoC) between classes and their dependencies.

For Blazor DI guidance, which adds to or supersedes the guidance in this article, seeASP.NET Core Blazor dependency injection.

For information specific to dependency injection within MVC controllers, seeDependency injection into controllers in ASP.NET Core.

For information on using dependency injection in applications other than web apps, seeDependency injection in .NET.

For information on dependency injection of options, seeOptions pattern in ASP.NET Core.

This article provides information on dependency injection in ASP.NET Core. The primary documentation on using dependency injection is contained inDependency injection in .NET.

View or download sample code (how to download)

Overview of dependency injection

Adependency is an object that another object depends on. Examine the followingMyDependency class with aWriteMessage method that other classes depend on:

public class MyDependency{    public void WriteMessage(string message)    {        Console.WriteLine($"MyDependency.WriteMessage called. Message: {message}");    }}

A class can create an instance of theMyDependency class to make use of itsWriteMessage method. In the following example, theMyDependency class is a dependency of theIndexModel class:

public class IndexModel : PageModel{    private readonly MyDependency _dependency = new MyDependency();    public void OnGet()    {        _dependency.WriteMessage("IndexModel.OnGet");    }}

The class creates and directly depends on theMyDependency class. Code dependencies, such as in the previous example, are problematic and should be avoided for the following reasons:

  • To replaceMyDependency with a different implementation, theIndexModel class must be modified.
  • IfMyDependency has dependencies, they must also be configured by theIndexModel class. In a large project with multiple classes depending onMyDependency, the configuration code becomes scattered across the app.
  • This implementation is difficult to unit test.

Dependency injection 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. ASP.NET Core provides a built-in service container,IServiceProvider. Services are typically registered in the app'sProgram.cs file.
  • Injection of the service into the constructor of the class where it's used. The framework takes on the responsibility of creating an instance of the dependency and disposing of it when it's no longer needed.

In thesample app, theIMyDependency interface defines theWriteMessage method:

public interface IMyDependency{    void WriteMessage(string message);}

This interface is implemented by a concrete type,MyDependency:

public class MyDependency : IMyDependency{    public void WriteMessage(string message)    {        Console.WriteLine($"MyDependency.WriteMessage Message: {message}");    }}

The sample app registers theIMyDependency service with the concrete typeMyDependency. TheAddScoped method registers the service with a scoped lifetime, the lifetime of a single request.Service lifetimes are described later in this article.

using DependencyInjectionSample.Interfaces;using DependencyInjectionSample.Services;var builder = WebApplication.CreateBuilder(args);builder.Services.AddRazorPages();builder.Services.AddScoped<IMyDependency, MyDependency>();var app = builder.Build();

In the sample app, theIMyDependency service is requested and used to call theWriteMessage method:

public class Index2Model : PageModel{    private readonly IMyDependency _myDependency;    public Index2Model(IMyDependency myDependency)    {        _myDependency = myDependency;                }    public void OnGet()    {        _myDependency.WriteMessage("Index2Model.OnGet");    }}

By using the DI pattern, the controller or Razor Page:

  • Doesn't use the concrete typeMyDependency, only theIMyDependency interface it implements. That makes it easy to change the implementation without modifying the controller or Razor Page.
  • Doesn't create an instance ofMyDependency, it's created by the DI container.

The implementation of theIMyDependency interface can be improved by using the built-in logging API:

public class MyDependency2 : IMyDependency{    private readonly ILogger<MyDependency2> _logger;    public MyDependency2(ILogger<MyDependency2> logger)    {        _logger = logger;    }    public void WriteMessage(string message)    {        _logger.LogInformation( $"MyDependency2.WriteMessage Message: {message}");    }}

The updatedProgram.cs registers the newIMyDependency implementation:

using DependencyInjectionSample.Interfaces;using DependencyInjectionSample.Services;var builder = WebApplication.CreateBuilder(args);builder.Services.AddRazorPages();builder.Services.AddScoped<IMyDependency, MyDependency2>();var app = builder.Build();

MyDependency2 depends onILogger<TCategoryName>, which it requests in the constructor.ILogger<TCategoryName> is aframework-provided service.

It's not unusual to use dependency injection 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 adependency tree,dependency graph, orobject graph.

The container resolvesILogger<TCategoryName> by taking advantage of(generic) open types, eliminating the need to register every(generic) constructed type.

In dependency injection terminology, a service:

  • Is typically an object that provides a service to other objects, such as theIMyDependency service.
  • Is not related to a web service, although the service might use a web service.

The framework provides a robustlogging system. TheIMyDependency implementations shown in the preceding examples were written to demonstrate basic DI, not to implement logging. Most apps shouldn't need to write loggers. The following code demonstrates using the default logging, which doesn't require any services to be registered:

public class AboutModel : PageModel{    private readonly ILogger _logger;    public AboutModel(ILogger<AboutModel> logger)    {        _logger = logger;    }        public string Message { get; set; } = string.Empty;    public void OnGet()    {        Message = $"About page visited at {DateTime.UtcNow.ToLongTimeString()}";        _logger.LogInformation(Message);    }}

The preceding code works correctly without changing anything inProgram.cs, becauselogging is provided by the framework.

Register groups of services with extension methods

The ASP.NET Core framework uses a convention for registering a group of related services. The convention is to use a singleAdd{GROUP_NAME} extension method to register all of the services required by a framework feature. For example, theAddControllers extension method registers the services required for MVC controllers.

The following code is generated by the Razor Pages template using individual accounts and shows how to add additional services to the container using the extension methodsAddDbContext andAddDefaultIdentity:

using DependencyInjectionSample.Data;using Microsoft.AspNetCore.Identity;using Microsoft.EntityFrameworkCore;var builder = WebApplication.CreateBuilder(args);var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");builder.Services.AddDbContext<ApplicationDbContext>(options =>    options.UseSqlServer(connectionString));builder.Services.AddDatabaseDeveloperPageExceptionFilter();builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)    .AddEntityFrameworkStores<ApplicationDbContext>();builder.Services.AddRazorPages();var app = builder.Build();

Consider the following which registers services and configures options:

using ConfigSample.Options;using Microsoft.Extensions.DependencyInjection.ConfigSample.Options;var builder = WebApplication.CreateBuilder(args);builder.Services.AddRazorPages();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>();var app = builder.Build();

Related groups of registrations can be moved to an extension method to register services. For example, the configuration services are added to the following class:

using ConfigSample.Options;using Microsoft.Extensions.Configuration;namespace Microsoft.Extensions.DependencyInjection{    public static class MyConfigServiceCollectionExtensions    {        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 AddMyDependencyGroup(             this IServiceCollection services)        {            services.AddScoped<IMyDependency, MyDependency>();            services.AddScoped<IMyDependency2, MyDependency2>();            return services;        }    }}

The remaining services are registered in a similar class. The following code uses the new extension methods to register the services:

using Microsoft.Extensions.DependencyInjection.ConfigSample.Options;var builder = WebApplication.CreateBuilder(args);builder.Services    .AddConfig(builder.Configuration)    .AddMyDependencyGroup();builder.Services.AddRazorPages();var app = builder.Build();

Note: Eachservices.Add{GROUP_NAME} extension method adds and potentially configures services. For example,AddControllersWithViews adds the services MVC controllers with views require, andAddRazorPages adds the services Razor Pages requires.

Service lifetimes

SeeService lifetimes inDependency injection in .NET

To use scoped services in middleware, use one of the following approaches:

  • Inject the service into the middleware'sInvoke orInvokeAsync method. Usingconstructor injection throws a runtime exception because it forces the scoped service to behave like a singleton. The sample in theLifetime and registration options section demonstrates theInvokeAsync approach.
  • UseFactory-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, seeWrite custom ASP.NET Core middleware.

Service registration methods

SeeService registration methods inDependency injection in .NET

It's common to use multiple implementations whenmocking types for testing.

Registering a service with only an implementation type is equivalent to registering that service with the same implementation and service type. This is why multiple implementations of a service can't be registered using the methods that don't take an explicit service type. These methods can register multipleinstances of a service, but they all have the sameimplementation type.

Any of these service registration methods can be used to register multiple service instances of the same service type. In the following example,AddSingleton is called twice withIMyDependency as the service type. The second call toAddSingleton overrides the previous one when resolved asIMyDependency and adds to the previous one when multiple services are resolved viaIEnumerable<IMyDependency>. Services appear in the order they were registered when resolved viaIEnumerable<{SERVICE}>.

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);    }}

Keyed services

The termkeyed services refers to a mechanism for registering and retrieving Dependency Injection (DI) services using keys. A service is associated with a key by callingAddKeyedSingleton (orAddKeyedScoped orAddKeyedTransient) to register it. Access a registered service by specifying the key with the[FromKeyedServices] attribute. The following code shows how to use keyed services:

using Microsoft.AspNetCore.Mvc;using Microsoft.AspNetCore.SignalR;var builder = WebApplication.CreateBuilder(args);builder.Services.AddKeyedSingleton<ICache, BigCache>("big");builder.Services.AddKeyedSingleton<ICache, SmallCache>("small");builder.Services.AddControllers();var app = builder.Build();app.MapGet("/big", ([FromKeyedServices("big")] ICache bigCache) => bigCache.Get("date"));app.MapGet("/small", ([FromKeyedServices("small")] ICache smallCache) =>                                                               smallCache.Get("date"));app.MapControllers();app.Run();public interface ICache{    object Get(string key);}public class BigCache : ICache{    public object Get(string key) => $"Resolving {key} from big cache.";}public class SmallCache : ICache{    public object Get(string key) => $"Resolving {key} from small cache.";}[ApiController][Route("/cache")]public class CustomServicesApiController : Controller{    [HttpGet("big-cache")]    public ActionResult<object> GetOk([FromKeyedServices("big")] ICache cache)    {        return cache.Get("data-mvc");    }}public class MyHub : Hub{    public void Method([FromKeyedServices("small")] ICache cache)    {        Console.WriteLine(cache.Get("signalr"));    }}

Keyed services in Middleware

Middleware supports Keyed services in both the constructor and theInvoke/InvokeAsync method:

var builder = WebApplication.CreateBuilder(args);builder.Services.AddKeyedSingleton<MySingletonClass>("test");builder.Services.AddKeyedScoped<MyScopedClass>("test2");var app = builder.Build();app.UseMiddleware<MyMiddleware>();app.Run();internal class MyMiddleware{    private readonly RequestDelegate _next;    public MyMiddleware(RequestDelegate next,        [FromKeyedServices("test")] MySingletonClass service)    {        _next = next;    }    public Task Invoke(HttpContext context,        [FromKeyedServices("test2")]            MyScopedClass scopedService) => _next(context);}

For more information on creating Middleware, seeWrite custom ASP.NET Core middleware

Constructor injection behavior

SeeConstructor injection behavior inDependency injection in .NET

Entity Framework contexts

By default, Entity Framework contexts are added to the service container using thescoped lifetime because web app database operations are normally scoped to the client request. To use a different lifetime, specify the lifetime by using anAddDbContext 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:

public interface IOperation{    string OperationId { get; }}public interface IOperationTransient : IOperation { }public interface IOperationScoped : IOperation { }public interface IOperationSingleton : IOperation { }

The followingOperation class implements all of the preceding interfaces. TheOperation constructor generates a GUID and stores the last 4 characters in theOperationId property:

public class Operation : IOperationTransient, IOperationScoped, IOperationSingleton{    public Operation()    {        OperationId = Guid.NewGuid().ToString()[^4..];    }    public string OperationId { get; }}

The following code creates multiple registrations of theOperation class according to the named lifetimes:

var builder = WebApplication.CreateBuilder(args);builder.Services.AddRazorPages();builder.Services.AddTransient<IOperationTransient, Operation>();builder.Services.AddScoped<IOperationScoped, Operation>();builder.Services.AddSingleton<IOperationSingleton, Operation>();var app = builder.Build();if (!app.Environment.IsDevelopment()){    app.UseExceptionHandler("/Error");    app.UseHsts();}app.UseHttpsRedirection();app.UseStaticFiles();app.UseMyMiddleware();app.UseRouting();app.UseAuthorization();app.MapRazorPages();app.Run();

The sample app demonstrates object lifetimes both within and between requests. TheIndexModel and the middleware request each kind ofIOperation type and log theOperationId for each:

public class IndexModel : PageModel{    private readonly ILogger _logger;    private readonly IOperationTransient _transientOperation;    private readonly IOperationSingleton _singletonOperation;    private readonly IOperationScoped _scopedOperation;    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);    }}

Similar to theIndexModel, the middleware resolves the same services:

public class MyMiddleware{    private readonly RequestDelegate _next;    private readonly ILogger _logger;    private readonly IOperationSingleton _singletonOperation;    public MyMiddleware(RequestDelegate next, ILogger<MyMiddleware> logger,        IOperationSingleton singletonOperation)    {        _logger = logger;        _singletonOperation = singletonOperation;        _next = next;    }    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 static class MyMiddlewareExtensions{    public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)    {        return builder.UseMiddleware<MyMiddleware>();    }}

Scoped and transient services must be resolved in theInvokeAsync method:

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);}

The logger output shows:

  • Transient objects are always different. The transientOperationId value is different in theIndexModel and in the middleware.
  • Scoped objects are the same for a given request but differ across each new request.
  • Singleton objects are the same for every request.

To reduce the logging output, set "Logging:LogLevel:Microsoft:Error" in theappsettings.Development.json file:

{  "MyKey": "MyKey from appsettings.Development.json",  "Logging": {    "LogLevel": {      "Default": "Information",      "System": "Debug",      "Microsoft": "Error"    }  }}

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 builder = WebApplication.CreateBuilder(args);builder.Services.AddScoped<IMyDependency, MyDependency>();var app = builder.Build();using (var serviceScope = app.Services.CreateScope()){    var services = serviceScope.ServiceProvider;    var myDependency = services.GetRequiredService<IMyDependency>();    myDependency.WriteMessage("Call services from main");}app.MapGet("/", () => "Hello World!");app.Run();

Scope validation

SeeConstructor injection behavior inDependency injection in .NET

For more information, seeScope validation.

Request Services

Services and their dependencies within an ASP.NET Core request are exposed throughHttpContext.RequestServices.

The framework creates a scope per request, andRequestServices 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 fromRequestServices. Requesting dependencies as constructor parameters yields classes that are easier to test.

Design services for dependency injection

When designing services for dependency injection:

  • 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 theSingle 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 callsDispose for theIDisposable 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:

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;    }}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;    }}public interface IService3{    public void Write(string message);}public class Service3 : IService3, IDisposable{    private bool _disposed;    public Service3(string myKey)    {        MyKey = myKey;    }    public string MyKey { get; }    public void Write(string message)    {        Console.WriteLine($"Service3: {message}, MyKey = {MyKey}");    }    public void Dispose()    {        if (_disposed)            return;        Console.WriteLine("Service3.Dispose");        _disposed = true;    }}
using DIsample2.Services;var builder = WebApplication.CreateBuilder(args);builder.Services.AddRazorPages();builder.Services.AddScoped<Service1>();builder.Services.AddSingleton<Service2>();var myKey = builder.Configuration["MyKey"];builder.Services.AddSingleton<IService3>(sp => new Service3(myKey));var app = builder.Build();
public class IndexModel : PageModel{    private readonly Service1 _service1;    private readonly Service2 _service2;    private readonly IService3 _service3;    public IndexModel(Service1 service1, Service2 service2, IService3 service3)    {        _service1 = service1;        _service2 = service2;        _service3 = service3;    }    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.OnGetService2: IndexModel.OnGetService3: IndexModel.OnGet, MyKey = MyKey from appsettings.Development.jsonService1.Dispose

Services not created by the service container

Consider the following code:

var builder = WebApplication.CreateBuilder(args);builder.Services.AddRazorPages();builder.Services.AddSingleton(new Service1());builder.Services.AddSingleton(new Service2());

In 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 the services.

IDisposable guidance for Transient and shared instances

SeeIDisposable guidance for Transient and shared instance inDependency injection in .NET

Default service container replacement

SeeDefault service container replacement inDependency injection in .NET

Recommendations

SeeRecommendations inDependency injection in .NET

  • Avoid using theservice locator pattern. For example, don't invokeGetService to obtain a service instance when you can use DI instead:

    Incorrect:

    Incorrect code

    Correct:

    public class MyClass{    private readonly IOptionsMonitor<MyOptions> _optionsMonitor;    public MyClass(IOptionsMonitor<MyOptions> optionsMonitor)    {        _optionsMonitor = 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 mixInversion of Control strategies.

  • Avoid static access toHttpContext (for example,IHttpContextAccessor.HttpContext).

DI is analternative 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 multi-tenancy in DI

Orchard Core is an application framework for building modular, multitenant applications on ASP.NET Core. For more information, see theOrchard Core Documentation.

See theOrchard Core samples for examples of how to build modular and multi-tenant apps using just the Orchard Core Framework without any of its CMS-specific features.

Framework-provided services

Program.cs registers services that the app uses, including platform features, such as Entity Framework Core and ASP.NET Core MVC. Initially, theIServiceCollection provided toProgram.cs has services defined by the framework depending onhow the host was configured. For apps based on the ASP.NET Core templates, the framework registers more than 250 services.

The following table lists a small sample of these framework-registered services:

Service TypeLifetime
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactoryTransient
IHostApplicationLifetimeSingleton
IWebHostEnvironmentSingleton
Microsoft.AspNetCore.Hosting.IStartupSingleton
Microsoft.AspNetCore.Hosting.IStartupFilterTransient
Microsoft.AspNetCore.Hosting.Server.IServerSingleton
Microsoft.AspNetCore.Http.IHttpContextFactoryTransient
Microsoft.Extensions.Logging.ILogger<TCategoryName>Singleton
Microsoft.Extensions.Logging.ILoggerFactorySingleton
Microsoft.Extensions.ObjectPool.ObjectPoolProviderSingleton
Microsoft.Extensions.Options.IConfigureOptions<TOptions>Transient
Microsoft.Extensions.Options.IOptions<TOptions>Singleton
System.Diagnostics.DiagnosticSourceSingleton
System.Diagnostics.DiagnosticListenerSingleton

Additional resources

ByKirk Larkin,Steve Smith, andBrandon Dahler

ASP.NET Core supports the dependency injection (DI) software design pattern, which is a technique for achievingInversion of Control (IoC) between classes and their dependencies.

For more information specific to dependency injection within MVC controllers, seeDependency injection into controllers in ASP.NET Core.

For information on using dependency injection in applications other than web apps, seeDependency injection in .NET.

For more information on dependency injection of options, seeOptions pattern in ASP.NET Core.

This topic provides information on dependency injection in ASP.NET Core. The primary documentation on using dependency injection is contained inDependency injection in .NET.

View or download sample code (how to download)

Overview of dependency injection

Adependency is an object that another object depends on. Examine the followingMyDependency class with aWriteMessage method that other classes depend on:

public class MyDependency{    public void WriteMessage(string message)    {        Console.WriteLine($"MyDependency.WriteMessage called. Message: {message}");    }}

A class can create an instance of theMyDependency class to make use of itsWriteMessage method. In the following example, theMyDependency class is a dependency of theIndexModel class:

public class IndexModel : PageModel{    private readonly MyDependency _dependency = new MyDependency();    public void OnGet()    {        _dependency.WriteMessage("IndexModel.OnGet");    }}

The class creates and directly depends on theMyDependency class. Code dependencies, such as in the previous example, are problematic and should be avoided for the following reasons:

  • To replaceMyDependency with a different implementation, theIndexModel class must be modified.
  • IfMyDependency has dependencies, they must also be configured by theIndexModel class. In a large project with multiple classes depending onMyDependency, the configuration code becomes scattered across the app.
  • This implementation is difficult to unit test.

Dependency injection 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. ASP.NET Core provides a built-in service container,IServiceProvider. Services are typically registered in the app'sProgram.cs file.
  • Injection of the service into the constructor of the class where it's used. The framework takes on the responsibility of creating an instance of the dependency and disposing of it when it's no longer needed.

In thesample app, theIMyDependency interface defines theWriteMessage method:

public interface IMyDependency{    void WriteMessage(string message);}

This interface is implemented by a concrete type,MyDependency:

public class MyDependency : IMyDependency{    public void WriteMessage(string message)    {        Console.WriteLine($"MyDependency.WriteMessage Message: {message}");    }}

The sample app registers theIMyDependency service with the concrete typeMyDependency. TheAddScoped method registers the service with a scoped lifetime, the lifetime of a single request.Service lifetimes are described later in this topic.

using DependencyInjectionSample.Interfaces;using DependencyInjectionSample.Services;var builder = WebApplication.CreateBuilder(args);builder.Services.AddRazorPages();builder.Services.AddScoped<IMyDependency, MyDependency>();var app = builder.Build();

In the sample app, theIMyDependency service is requested and used to call theWriteMessage method:

public class Index2Model : PageModel{    private readonly IMyDependency _myDependency;    public Index2Model(IMyDependency myDependency)    {        _myDependency = myDependency;                }    public void OnGet()    {        _myDependency.WriteMessage("Index2Model.OnGet");    }}

By using the DI pattern, the controller or Razor Page:

  • Doesn't use the concrete typeMyDependency, only theIMyDependency interface it implements. That makes it easy to change the implementation without modifying the controller or Razor Page.
  • Doesn't create an instance ofMyDependency, it's created by the DI container.

The implementation of theIMyDependency interface can be improved by using the built-in logging API:

public class MyDependency2 : IMyDependency{    private readonly ILogger<MyDependency2> _logger;    public MyDependency2(ILogger<MyDependency2> logger)    {        _logger = logger;    }    public void WriteMessage(string message)    {        _logger.LogInformation( $"MyDependency2.WriteMessage Message: {message}");    }}

The updatedProgram.cs registers the newIMyDependency implementation:

using DependencyInjectionSample.Interfaces;using DependencyInjectionSample.Services;var builder = WebApplication.CreateBuilder(args);builder.Services.AddRazorPages();builder.Services.AddScoped<IMyDependency, MyDependency2>();var app = builder.Build();

MyDependency2 depends onILogger<TCategoryName>, which it requests in the constructor.ILogger<TCategoryName> is aframework-provided service.

It's not unusual to use dependency injection 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 adependency tree,dependency graph, orobject graph.

The container resolvesILogger<TCategoryName> by taking advantage of(generic) open types, eliminating the need to register every(generic) constructed type.

In dependency injection terminology, a service:

  • Is typically an object that provides a service to other objects, such as theIMyDependency service.
  • Is not related to a web service, although the service may use a web service.

The framework provides a robustlogging system. TheIMyDependency implementations shown in the preceding examples were written to demonstrate basic DI, not to implement logging. Most apps shouldn't need to write loggers. The following code demonstrates using the default logging, which doesn't require any services to be registered:

public class AboutModel : PageModel{    private readonly ILogger _logger;    public AboutModel(ILogger<AboutModel> logger)    {        _logger = logger;    }        public string Message { get; set; } = string.Empty;    public void OnGet()    {        Message = $"About page visited at {DateTime.UtcNow.ToLongTimeString()}";        _logger.LogInformation(Message);    }}

Using the preceding code, there is no need to updateProgram.cs, becauselogging is provided by the framework.

Register groups of services with extension methods

The ASP.NET Core framework uses a convention for registering a group of related services. The convention is to use a singleAdd{GROUP_NAME} extension method to register all of the services required by a framework feature. For example, theAddControllers extension method registers the services required for MVC controllers.

The following code is generated by the Razor Pages template using individual accounts and shows how to add additional services to the container using the extension methodsAddDbContext andAddDefaultIdentity:

using DependencyInjectionSample.Data;using Microsoft.AspNetCore.Identity;using Microsoft.EntityFrameworkCore;var builder = WebApplication.CreateBuilder(args);var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");builder.Services.AddDbContext<ApplicationDbContext>(options =>    options.UseSqlServer(connectionString));builder.Services.AddDatabaseDeveloperPageExceptionFilter();builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)    .AddEntityFrameworkStores<ApplicationDbContext>();builder.Services.AddRazorPages();var app = builder.Build();

Consider the following which registers services and configures options:

using ConfigSample.Options;using Microsoft.Extensions.DependencyInjection.ConfigSample.Options;var builder = WebApplication.CreateBuilder(args);builder.Services.AddRazorPages();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>();var app = builder.Build();

Related groups of registrations can be moved to an extension method to register services. For example, the configuration services are added to the following class:

using ConfigSample.Options;using Microsoft.Extensions.Configuration;namespace Microsoft.Extensions.DependencyInjection{    public static class MyConfigServiceCollectionExtensions    {        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 AddMyDependencyGroup(             this IServiceCollection services)        {            services.AddScoped<IMyDependency, MyDependency>();            services.AddScoped<IMyDependency2, MyDependency2>();            return services;        }    }}

The remaining services are registered in a similar class. The following code uses the new extension methods to register the services:

using Microsoft.Extensions.DependencyInjection.ConfigSample.Options;var builder = WebApplication.CreateBuilder(args);builder.Services    .AddConfig(builder.Configuration)    .AddMyDependencyGroup();builder.Services.AddRazorPages();var app = builder.Build();

Note: Eachservices.Add{GROUP_NAME} extension method adds and potentially configures services. For example,AddControllersWithViews adds the services MVC controllers with views require, andAddRazorPages adds the services Razor Pages requires.

Service lifetimes

SeeService lifetimes inDependency injection in .NET

To use scoped services in middleware, use one of the following approaches:

  • Inject the service into the middleware'sInvoke orInvokeAsync method. Usingconstructor injection throws a runtime exception because it forces the scoped service to behave like a singleton. The sample in theLifetime and registration options section demonstrates theInvokeAsync approach.
  • UseFactory-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, seeWrite custom ASP.NET Core middleware.

Service registration methods

SeeService registration methods inDependency injection in .NET

It's common to use multiple implementations whenmocking types for testing.

Registering a service with only an implementation type is equivalent to registering that service with the same implementation and service type. This is why multiple implementations of a service cannot be registered using the methods that don't take an explicit service type. These methods can register multipleinstances of a service, but they will all have the sameimplementation type.

Any of the above service registration methods can be used to register multiple service instances of the same service type. In the following example,AddSingleton is called twice withIMyDependency as the service type. The second call toAddSingleton overrides the previous one when resolved asIMyDependency and adds to the previous one when multiple services are resolved viaIEnumerable<IMyDependency>. Services appear in the order they were registered when resolved viaIEnumerable<{SERVICE}>.

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);    }}

Keyed services

Keyed services refers to a mechanism for registering and retrieving Dependency Injection (DI) services using keys. A service is associated with a key by callingAddKeyedSingleton (orAddKeyedScoped orAddKeyedTransient) to register it. Access a registered service by specifying the key with the[FromKeyedServices] attribute. The following code shows how to use keyed services:

using Microsoft.AspNetCore.Mvc;using Microsoft.AspNetCore.SignalR;var builder = WebApplication.CreateBuilder(args);builder.Services.AddKeyedSingleton<ICache, BigCache>("big");builder.Services.AddKeyedSingleton<ICache, SmallCache>("small");builder.Services.AddControllers();var app = builder.Build();app.MapGet("/big", ([FromKeyedServices("big")] ICache bigCache) => bigCache.Get("date"));app.MapGet("/small", ([FromKeyedServices("small")] ICache smallCache) =>                                                               smallCache.Get("date"));app.MapControllers();app.Run();public interface ICache{    object Get(string key);}public class BigCache : ICache{    public object Get(string key) => $"Resolving {key} from big cache.";}public class SmallCache : ICache{    public object Get(string key) => $"Resolving {key} from small cache.";}[ApiController][Route("/cache")]public class CustomServicesApiController : Controller{    [HttpGet("big-cache")]    public ActionResult<object> GetOk([FromKeyedServices("big")] ICache cache)    {        return cache.Get("data-mvc");    }}public class MyHub : Hub{    public void Method([FromKeyedServices("small")] ICache cache)    {        Console.WriteLine(cache.Get("signalr"));    }}

Constructor injection behavior

SeeConstructor injection behavior inDependency injection in .NET

Entity Framework contexts

By default, Entity Framework contexts are added to the service container using thescoped lifetime because web app database operations are normally scoped to the client request. To use a different lifetime, specify the lifetime by using anAddDbContext 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:

public interface IOperation{    string OperationId { get; }}public interface IOperationTransient : IOperation { }public interface IOperationScoped : IOperation { }public interface IOperationSingleton : IOperation { }

The followingOperation class implements all of the preceding interfaces. TheOperation constructor generates a GUID and stores the last 4 characters in theOperationId property:

public class Operation : IOperationTransient, IOperationScoped, IOperationSingleton{    public Operation()    {        OperationId = Guid.NewGuid().ToString()[^4..];    }    public string OperationId { get; }}

The following code creates multiple registrations of theOperation class according to the named lifetimes:

var builder = WebApplication.CreateBuilder(args);builder.Services.AddRazorPages();builder.Services.AddTransient<IOperationTransient, Operation>();builder.Services.AddScoped<IOperationScoped, Operation>();builder.Services.AddSingleton<IOperationSingleton, Operation>();var app = builder.Build();if (!app.Environment.IsDevelopment()){    app.UseExceptionHandler("/Error");    app.UseHsts();}app.UseHttpsRedirection();app.UseStaticFiles();app.UseMyMiddleware();app.UseRouting();app.UseAuthorization();app.MapRazorPages();app.Run();

The sample app demonstrates object lifetimes both within and between requests. TheIndexModel and the middleware request each kind ofIOperation type and log theOperationId for each:

public class IndexModel : PageModel{    private readonly ILogger _logger;    private readonly IOperationTransient _transientOperation;    private readonly IOperationSingleton _singletonOperation;    private readonly IOperationScoped _scopedOperation;    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);    }}

Similar to theIndexModel, the middleware resolves the same services:

public class MyMiddleware{    private readonly RequestDelegate _next;    private readonly ILogger _logger;    private readonly IOperationSingleton _singletonOperation;    public MyMiddleware(RequestDelegate next, ILogger<MyMiddleware> logger,        IOperationSingleton singletonOperation)    {        _logger = logger;        _singletonOperation = singletonOperation;        _next = next;    }    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 static class MyMiddlewareExtensions{    public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)    {        return builder.UseMiddleware<MyMiddleware>();    }}

Scoped and transient services must be resolved in theInvokeAsync method:

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);}

The logger output shows:

  • Transient objects are always different. The transientOperationId value is different in theIndexModel and in the middleware.
  • Scoped objects are the same for a given request but differ across each new request.
  • Singleton objects are the same for every request.

To reduce the logging output, set "Logging:LogLevel:Microsoft:Error" in theappsettings.Development.json file:

{  "MyKey": "MyKey from appsettings.Development.json",  "Logging": {    "LogLevel": {      "Default": "Information",      "System": "Debug",      "Microsoft": "Error"    }  }}

Resolve a service at app start up

The following code shows how to resolve a scoped service for a limited duration when the app starts:

var builder = WebApplication.CreateBuilder(args);builder.Services.AddScoped<IMyDependency, MyDependency>();var app = builder.Build();using (var serviceScope = app.Services.CreateScope()){    var services = serviceScope.ServiceProvider;    var myDependency = services.GetRequiredService<IMyDependency>();    myDependency.WriteMessage("Call services from main");}app.MapGet("/", () => "Hello World!");app.Run();

Scope validation

SeeConstructor injection behavior inDependency injection in .NET

For more information, seeScope validation.

Request Services

Services and their dependencies within an ASP.NET Core request are exposed throughHttpContext.RequestServices.

The framework creates a scope per request, andRequestServices 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 fromRequestServices. Requesting dependencies as constructor parameters yields classes that are easier to test.

Design services for dependency injection

When designing services for dependency injection:

  • 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 a lot of injected dependencies, it might be a sign that the class has too many responsibilities and violates theSingle 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 callsDispose for theIDisposable 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:dependency-injection\samples\6.x\DIsample2\DIsample2\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;    }}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;    }}public interface IService3{    public void Write(string message);}public class Service3 : IService3, IDisposable{    private bool _disposed;    public Service3(string myKey)    {        MyKey = myKey;    }    public string MyKey { get; }    public void Write(string message)    {        Console.WriteLine($"Service3: {message}, MyKey = {MyKey}");    }    public void Dispose()    {        if (_disposed)            return;        Console.WriteLine("Service3.Dispose");        _disposed = true;    }}
using DIsample2.Services;var builder = WebApplication.CreateBuilder(args);builder.Services.AddRazorPages();builder.Services.AddScoped<Service1>();builder.Services.AddSingleton<Service2>();var myKey = builder.Configuration["MyKey"];builder.Services.AddSingleton<IService3>(sp => new Service3(myKey));var app = builder.Build();
public class IndexModel : PageModel{    private readonly Service1 _service1;    private readonly Service2 _service2;    private readonly IService3 _service3;    public IndexModel(Service1 service1, Service2 service2, IService3 service3)    {        _service1 = service1;        _service2 = service2;        _service3 = service3;    }    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.OnGetService2: IndexModel.OnGetService3: IndexModel.OnGet, MyKey = MyKey from appsettings.Development.jsonService1.Dispose

Services not created by the service container

Consider the following code:

var builder = WebApplication.CreateBuilder(args);builder.Services.AddRazorPages();builder.Services.AddSingleton(new Service1());builder.Services.AddSingleton(new Service2());

In 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 the services.

IDisposable guidance for Transient and shared instances

SeeIDisposable guidance for Transient and shared instance inDependency injection in .NET

Default service container replacement

SeeDefault service container replacement inDependency injection in .NET

Recommendations

SeeRecommendations inDependency injection in .NET

  • Avoid using theservice locator pattern. For example, don't invokeGetService to obtain a service instance when you can use DI instead:

    Incorrect:

    Incorrect code

    Correct:

    public class MyClass{    private readonly IOptionsMonitor<MyOptions> _optionsMonitor;    public MyClass(IOptionsMonitor<MyOptions> optionsMonitor)    {        _optionsMonitor = 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 mixInversion of Control strategies.

  • Avoid static access toHttpContext (for example,IHttpContextAccessor.HttpContext).

DI is analternative to static/global object access patterns. You may not be able to realize the benefits of DI if you mix it with static object access.

Recommended patterns for multi-tenancy in DI

Orchard Core is an application framework for building modular, multi-tenant applications on ASP.NET Core. For more information, see theOrchard Core Documentation.

See theOrchard Core samples for examples of how to build modular and multi-tenant apps using just the Orchard Core Framework without any of its CMS-specific features.

Framework-provided services

Program.cs registers services that the app uses, including platform features, such as Entity Framework Core and ASP.NET Core MVC. Initially, theIServiceCollection provided toProgram.cs has services defined by the framework depending onhow the host was configured. For apps based on the ASP.NET Core templates, the framework registers more than 250 services.

The following table lists a small sample of these framework-registered services:

Service TypeLifetime
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactoryTransient
IHostApplicationLifetimeSingleton
IWebHostEnvironmentSingleton
Microsoft.AspNetCore.Hosting.IStartupSingleton
Microsoft.AspNetCore.Hosting.IStartupFilterTransient
Microsoft.AspNetCore.Hosting.Server.IServerSingleton
Microsoft.AspNetCore.Http.IHttpContextFactoryTransient
Microsoft.Extensions.Logging.ILogger<TCategoryName>Singleton
Microsoft.Extensions.Logging.ILoggerFactorySingleton
Microsoft.Extensions.ObjectPool.ObjectPoolProviderSingleton
Microsoft.Extensions.Options.IConfigureOptions<TOptions>Transient
Microsoft.Extensions.Options.IOptions<TOptions>Singleton
System.Diagnostics.DiagnosticSourceSingleton
System.Diagnostics.DiagnosticListenerSingleton

Additional resources

ByKirk Larkin,Steve Smith, andBrandon Dahler

ASP.NET Core supports the dependency injection (DI) software design pattern, which is a technique for achievingInversion of Control (IoC) between classes and their dependencies.

For more information specific to dependency injection within MVC controllers, seeDependency injection into controllers in ASP.NET Core.

For information on using dependency injection in applications other than web apps, seeDependency injection in .NET.

For more information on dependency injection of options, seeOptions pattern in ASP.NET Core.

This topic provides information on dependency injection in ASP.NET Core. The primary documentation on using dependency injection is contained inDependency injection in .NET.

View or download sample code (how to download)

Overview of dependency injection

Adependency is an object that another object depends on. Examine the followingMyDependency class with aWriteMessage method that other classes depend on:

public class MyDependency{    public void WriteMessage(string message)    {        Console.WriteLine($"MyDependency.WriteMessage called. Message: {message}");    }}

A class can create an instance of theMyDependency class to make use of itsWriteMessage method. In the following example, theMyDependency class is a dependency of theIndexModel class:

public class IndexModel : PageModel{    private readonly MyDependency _dependency = new MyDependency();    public void OnGet()    {        _dependency.WriteMessage("IndexModel.OnGet");    }}

The class creates and directly depends on theMyDependency class. Code dependencies, such as in the previous example, are problematic and should be avoided for the following reasons:

  • To replaceMyDependency with a different implementation, theIndexModel class must be modified.
  • IfMyDependency has dependencies, they must also be configured by theIndexModel class. In a large project with multiple classes depending onMyDependency, the configuration code becomes scattered across the app.
  • This implementation is difficult to unit test.

Dependency injection 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. ASP.NET Core provides a built-in service container,IServiceProvider. Services are typically registered in the app'sProgram.cs file.
  • Injection of the service into the constructor of the class where it's used. The framework takes on the responsibility of creating an instance of the dependency and disposing of it when it's no longer needed.

In thesample app, theIMyDependency interface defines theWriteMessage method:

public interface IMyDependency{    void WriteMessage(string message);}

This interface is implemented by a concrete type,MyDependency:

public class MyDependency : IMyDependency{    public void WriteMessage(string message)    {        Console.WriteLine($"MyDependency.WriteMessage Message: {message}");    }}

The sample app registers theIMyDependency service with the concrete typeMyDependency. TheAddScoped method registers the service with a scoped lifetime, the lifetime of a single request.Service lifetimes are described later in this topic.

using DependencyInjectionSample.Interfaces;using DependencyInjectionSample.Services;var builder = WebApplication.CreateBuilder(args);builder.Services.AddRazorPages();builder.Services.AddScoped<IMyDependency, MyDependency>();var app = builder.Build();

In the sample app, theIMyDependency service is requested and used to call theWriteMessage method:

public class Index2Model : PageModel{    private readonly IMyDependency _myDependency;    public Index2Model(IMyDependency myDependency)    {        _myDependency = myDependency;                }    public void OnGet()    {        _myDependency.WriteMessage("Index2Model.OnGet");    }}

By using the DI pattern, the controller or Razor Page:

  • Doesn't use the concrete typeMyDependency, only theIMyDependency interface it implements. That makes it easy to change the implementation without modifying the controller or Razor Page.
  • Doesn't create an instance ofMyDependency, it's created by the DI container.

The implementation of theIMyDependency interface can be improved by using the built-in logging API:

public class MyDependency2 : IMyDependency{    private readonly ILogger<MyDependency2> _logger;    public MyDependency2(ILogger<MyDependency2> logger)    {        _logger = logger;    }    public void WriteMessage(string message)    {        _logger.LogInformation( $"MyDependency2.WriteMessage Message: {message}");    }}

The updatedProgram.cs registers the newIMyDependency implementation:

using DependencyInjectionSample.Interfaces;using DependencyInjectionSample.Services;var builder = WebApplication.CreateBuilder(args);builder.Services.AddRazorPages();builder.Services.AddScoped<IMyDependency, MyDependency2>();var app = builder.Build();

MyDependency2 depends onILogger<TCategoryName>, which it requests in the constructor.ILogger<TCategoryName> is aframework-provided service.

It's not unusual to use dependency injection 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 adependency tree,dependency graph, orobject graph.

The container resolvesILogger<TCategoryName> by taking advantage of(generic) open types, eliminating the need to register every(generic) constructed type.

In dependency injection terminology, a service:

  • Is typically an object that provides a service to other objects, such as theIMyDependency service.
  • Is not related to a web service, although the service may use a web service.

The framework provides a robustlogging system. TheIMyDependency implementations shown in the preceding examples were written to demonstrate basic DI, not to implement logging. Most apps shouldn't need to write loggers. The following code demonstrates using the default logging, which doesn't require any services to be registered:

public class AboutModel : PageModel{    private readonly ILogger _logger;    public AboutModel(ILogger<AboutModel> logger)    {        _logger = logger;    }        public string Message { get; set; } = string.Empty;    public void OnGet()    {        Message = $"About page visited at {DateTime.UtcNow.ToLongTimeString()}";        _logger.LogInformation(Message);    }}

Using the preceding code, there is no need to updateProgram.cs, becauselogging is provided by the framework.

Register groups of services with extension methods

The ASP.NET Core framework uses a convention for registering a group of related services. The convention is to use a singleAdd{GROUP_NAME} extension method to register all of the services required by a framework feature. For example, theAddControllers extension method registers the services required for MVC controllers.

The following code is generated by the Razor Pages template using individual user accounts and shows how to add additional services to the container using the extension methodsAddDbContext andAddDefaultIdentity:

using DependencyInjectionSample.Data;using Microsoft.AspNetCore.Identity;using Microsoft.EntityFrameworkCore;var builder = WebApplication.CreateBuilder(args);var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");builder.Services.AddDbContext<ApplicationDbContext>(options =>    options.UseSqlServer(connectionString));builder.Services.AddDatabaseDeveloperPageExceptionFilter();builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)    .AddEntityFrameworkStores<ApplicationDbContext>();builder.Services.AddRazorPages();var app = builder.Build();

Consider the following which registers services and configures options:

using ConfigSample.Options;using Microsoft.Extensions.DependencyInjection.ConfigSample.Options;var builder = WebApplication.CreateBuilder(args);builder.Services.AddRazorPages();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>();var app = builder.Build();

Related groups of registrations can be moved to an extension method to register services. For example, the configuration services are added to the following class:

using ConfigSample.Options;using Microsoft.Extensions.Configuration;namespace Microsoft.Extensions.DependencyInjection{    public static class MyConfigServiceCollectionExtensions    {        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 AddMyDependencyGroup(             this IServiceCollection services)        {            services.AddScoped<IMyDependency, MyDependency>();            services.AddScoped<IMyDependency2, MyDependency2>();            return services;        }    }}

The remaining services are registered in a similar class. The following code uses the new extension methods to register the services:

using Microsoft.Extensions.DependencyInjection.ConfigSample.Options;var builder = WebApplication.CreateBuilder(args);builder.Services    .AddConfig(builder.Configuration)    .AddMyDependencyGroup();builder.Services.AddRazorPages();var app = builder.Build();

Note: Eachservices.Add{GROUP_NAME} extension method adds and potentially configures services. For example,AddControllersWithViews adds the services MVC controllers with views require, andAddRazorPages adds the services Razor Pages requires.

Service lifetimes

SeeService lifetimes inDependency injection in .NET

To use scoped services in middleware, use one of the following approaches:

  • Inject the service into the middleware'sInvoke orInvokeAsync method. Usingconstructor injection throws a runtime exception because it forces the scoped service to behave like a singleton. The sample in theLifetime and registration options section demonstrates theInvokeAsync approach.
  • UseFactory-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, seeWrite custom ASP.NET Core middleware.

Service registration methods

SeeService registration methods inDependency injection in .NET

It's common to use multiple implementations whenmocking types for testing.

Registering a service with only an implementation type is equivalent to registering that service with the same implementation and service type. This is why multiple implementations of a service cannot be registered using the methods that don't take an explicit service type. These methods can register multipleinstances of a service, but they will all have the sameimplementation type.

Any of the above service registration methods can be used to register multiple service instances of the same service type. In the following example,AddSingleton is called twice withIMyDependency as the service type. The second call toAddSingleton overrides the previous one when resolved asIMyDependency and adds to the previous one when multiple services are resolved viaIEnumerable<IMyDependency>. Services appear in the order they were registered when resolved viaIEnumerable<{SERVICE}>.

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);    }}

Constructor injection behavior

SeeConstructor injection behavior inDependency injection in .NET

Entity Framework contexts

By default, Entity Framework contexts are added to the service container using thescoped lifetime because web app database operations are normally scoped to the client request. To use a different lifetime, specify the lifetime by using anAddDbContext 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:

public interface IOperation{    string OperationId { get; }}public interface IOperationTransient : IOperation { }public interface IOperationScoped : IOperation { }public interface IOperationSingleton : IOperation { }

The followingOperation class implements all of the preceding interfaces. TheOperation constructor generates a GUID and stores the last 4 characters in theOperationId property:

public class Operation : IOperationTransient, IOperationScoped, IOperationSingleton{    public Operation()    {        OperationId = Guid.NewGuid().ToString()[^4..];    }    public string OperationId { get; }}

The following code creates multiple registrations of theOperation class according to the named lifetimes:

var builder = WebApplication.CreateBuilder(args);builder.Services.AddRazorPages();builder.Services.AddTransient<IOperationTransient, Operation>();builder.Services.AddScoped<IOperationScoped, Operation>();builder.Services.AddSingleton<IOperationSingleton, Operation>();var app = builder.Build();if (!app.Environment.IsDevelopment()){    app.UseExceptionHandler("/Error");    app.UseHsts();}app.UseHttpsRedirection();app.UseStaticFiles();app.UseMyMiddleware();app.UseRouting();app.UseAuthorization();app.MapRazorPages();app.Run();

The sample app demonstrates object lifetimes both within and between requests. TheIndexModel and the middleware request each kind ofIOperation type and log theOperationId for each:

public class IndexModel : PageModel{    private readonly ILogger _logger;    private readonly IOperationTransient _transientOperation;    private readonly IOperationSingleton _singletonOperation;    private readonly IOperationScoped _scopedOperation;    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);    }}

Similar to theIndexModel, the middleware resolves the same services:

public class MyMiddleware{    private readonly RequestDelegate _next;    private readonly ILogger _logger;    private readonly IOperationSingleton _singletonOperation;    public MyMiddleware(RequestDelegate next, ILogger<MyMiddleware> logger,        IOperationSingleton singletonOperation)    {        _logger = logger;        _singletonOperation = singletonOperation;        _next = next;    }    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 static class MyMiddlewareExtensions{    public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)    {        return builder.UseMiddleware<MyMiddleware>();    }}

Scoped and transient services must be resolved in theInvokeAsync method:

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);}

The logger output shows:

  • Transient objects are always different. The transientOperationId value is different in theIndexModel and in the middleware.
  • Scoped objects are the same for a given request but differ across each new request.
  • Singleton objects are the same for every request.

To reduce the logging output, set "Logging:LogLevel:Microsoft:Error" in theappsettings.Development.json file:

{  "MyKey": "MyKey from appsettings.Development.json",  "Logging": {    "LogLevel": {      "Default": "Information",      "System": "Debug",      "Microsoft": "Error"    }  }}

Resolve a service at app start up

The following code shows how to resolve a scoped service for a limited duration when the app starts:

var builder = WebApplication.CreateBuilder(args);builder.Services.AddScoped<IMyDependency, MyDependency>();var app = builder.Build();using (var serviceScope = app.Services.CreateScope()){    var services = serviceScope.ServiceProvider;    var myDependency = services.GetRequiredService<IMyDependency>();    myDependency.WriteMessage("Call services from main");}app.MapGet("/", () => "Hello World!");app.Run();

Scope validation

SeeConstructor injection behavior inDependency injection in .NET

For more information, seeScope validation.

Request Services

Services and their dependencies within an ASP.NET Core request are exposed throughHttpContext.RequestServices.

The framework creates a scope per request, andRequestServices 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 fromRequestServices. Requesting dependencies as constructor parameters yields classes that are easier to test.

Design services for dependency injection

When designing services for dependency injection:

  • 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 a lot of injected dependencies, it might be a sign that the class has too many responsibilities and violates theSingle 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 callsDispose for theIDisposable 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:dependency-injection\samples\6.x\DIsample2\DIsample2\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;    }}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;    }}public interface IService3{    public void Write(string message);}public class Service3 : IService3, IDisposable{    private bool _disposed;    public Service3(string myKey)    {        MyKey = myKey;    }    public string MyKey { get; }    public void Write(string message)    {        Console.WriteLine($"Service3: {message}, MyKey = {MyKey}");    }    public void Dispose()    {        if (_disposed)            return;        Console.WriteLine("Service3.Dispose");        _disposed = true;    }}
using DIsample2.Services;var builder = WebApplication.CreateBuilder(args);builder.Services.AddRazorPages();builder.Services.AddScoped<Service1>();builder.Services.AddSingleton<Service2>();var myKey = builder.Configuration["MyKey"];builder.Services.AddSingleton<IService3>(sp => new Service3(myKey));var app = builder.Build();
public class IndexModel : PageModel{    private readonly Service1 _service1;    private readonly Service2 _service2;    private readonly IService3 _service3;    public IndexModel(Service1 service1, Service2 service2, IService3 service3)    {        _service1 = service1;        _service2 = service2;        _service3 = service3;    }    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.OnGetService2: IndexModel.OnGetService3: IndexModel.OnGet, MyKey = MyKey from appsettings.Development.jsonService1.Dispose

Services not created by the service container

Consider the following code:

var builder = WebApplication.CreateBuilder(args);builder.Services.AddRazorPages();builder.Services.AddSingleton(new Service1());builder.Services.AddSingleton(new Service2());

In 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 the services.

IDisposable guidance for Transient and shared instances

SeeIDisposable guidance for Transient and shared instance inDependency injection in .NET

Default service container replacement

SeeDefault service container replacement inDependency injection in .NET

Recommendations

SeeRecommendations inDependency injection in .NET

  • Avoid using theservice locator pattern. For example, don't invokeGetService to obtain a service instance when you can use DI instead:

    Incorrect:

    Incorrect code

    Correct:

    public class MyClass{    private readonly IOptionsMonitor<MyOptions> _optionsMonitor;    public MyClass(IOptionsMonitor<MyOptions> optionsMonitor)    {        _optionsMonitor = 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 mixInversion of Control strategies.

  • Avoid static access toHttpContext (for example,IHttpContextAccessor.HttpContext).

DI is analternative to static/global object access patterns. You may not be able to realize the benefits of DI if you mix it with static object access.

Recommended patterns for multi-tenancy in DI

Orchard Core is an application framework for building modular, multi-tenant applications on ASP.NET Core. For more information, see theOrchard Core Documentation.

See theOrchard Core samples for examples of how to build modular and multi-tenant apps using just the Orchard Core Framework without any of its CMS-specific features.

Framework-provided services

Program.cs registers services that the app uses, including platform features, such as Entity Framework Core and ASP.NET Core MVC. Initially, theIServiceCollection provided toProgram.cs has services defined by the framework depending onhow the host was configured. For apps based on the ASP.NET Core templates, the framework registers more than 250 services.

The following table lists a small sample of these framework-registered services:

Service TypeLifetime
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactoryTransient
IHostApplicationLifetimeSingleton
IWebHostEnvironmentSingleton
Microsoft.AspNetCore.Hosting.IStartupSingleton
Microsoft.AspNetCore.Hosting.IStartupFilterTransient
Microsoft.AspNetCore.Hosting.Server.IServerSingleton
Microsoft.AspNetCore.Http.IHttpContextFactoryTransient
Microsoft.Extensions.Logging.ILogger<TCategoryName>Singleton
Microsoft.Extensions.Logging.ILoggerFactorySingleton
Microsoft.Extensions.ObjectPool.ObjectPoolProviderSingleton
Microsoft.Extensions.Options.IConfigureOptions<TOptions>Transient
Microsoft.Extensions.Options.IOptions<TOptions>Singleton
System.Diagnostics.DiagnosticSourceSingleton
System.Diagnostics.DiagnosticListenerSingleton

Additional resources

ByKirk Larkin,Steve Smith,Scott Addie, andBrandon Dahler

ASP.NET Core supports the dependency injection (DI) software design pattern, which is a technique for achievingInversion of Control (IoC) between classes and their dependencies.

For more information specific to dependency injection within MVC controllers, seeDependency injection into controllers in ASP.NET Core.

For information on using dependency injection in applications other than web apps, seeDependency injection in .NET.

For more information on dependency injection of options, seeOptions pattern in ASP.NET Core.

This topic provides information on dependency injection in ASP.NET Core. The primary documentation on using dependency injection is contained inDependency injection in .NET.

View or download sample code (how to download)

Overview of dependency injection

Adependency is an object that another object depends on. Examine the followingMyDependency class with aWriteMessage method that other classes depend on:

public class MyDependency{    public void WriteMessage(string message)    {        Console.WriteLine($"MyDependency.WriteMessage called. Message: {message}");    }}

A class can create an instance of theMyDependency class to make use of itsWriteMessage method. In the following example, theMyDependency class is a dependency of theIndexModel class:

public class IndexModel : PageModel{    private readonly MyDependency _dependency = new MyDependency();    public void OnGet()    {        _dependency.WriteMessage("IndexModel.OnGet created this message.");    }}

The class creates and directly depends on theMyDependency class. Code dependencies, such as in the previous example, are problematic and should be avoided for the following reasons:

  • To replaceMyDependency with a different implementation, theIndexModel class must be modified.
  • IfMyDependency has dependencies, they must also be configured by theIndexModel class. In a large project with multiple classes depending onMyDependency, the configuration code becomes scattered across the app.
  • This implementation is difficult to unit test. The app should use a mock or stubMyDependency class, which isn't possible with this approach.

Dependency injection 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. ASP.NET Core provides a built-in service container,IServiceProvider. Services are typically registered in the app'sStartup.ConfigureServices method.
  • Injection of the service into the constructor of the class where it's used. The framework takes on the responsibility of creating an instance of the dependency and disposing of it when it's no longer needed.

In thesample app, theIMyDependency interface defines theWriteMessage method:

public interface IMyDependency{    void WriteMessage(string message);}

This interface is implemented by a concrete type,MyDependency:

public class MyDependency : IMyDependency{    public void WriteMessage(string message)    {        Console.WriteLine($"MyDependency.WriteMessage Message: {message}");    }}

The sample app registers theIMyDependency service with the concrete typeMyDependency. TheAddScoped method registers the service with a scoped lifetime, the lifetime of a single request.Service lifetimes are described later in this topic.

public void ConfigureServices(IServiceCollection services){    services.AddScoped<IMyDependency, MyDependency>();    services.AddRazorPages();}

In the sample app, theIMyDependency service is requested and used to call theWriteMessage method:

public class Index2Model : PageModel{    private readonly IMyDependency _myDependency;    public Index2Model(IMyDependency myDependency)    {        _myDependency = myDependency;                }    public void OnGet()    {        _myDependency.WriteMessage("Index2Model.OnGet");    }}

By using the DI pattern, the controller:

  • Doesn't use the concrete typeMyDependency, only theIMyDependency interface it implements. That makes it easy to change the implementation that the controller uses without modifying the controller.
  • Doesn't create an instance ofMyDependency, it's created by the DI container.

The implementation of theIMyDependency interface can be improved by using the built-in logging API:

public class MyDependency2 : IMyDependency{    private readonly ILogger<MyDependency2> _logger;    public MyDependency2(ILogger<MyDependency2> logger)    {        _logger = logger;    }    public void WriteMessage(string message)    {        _logger.LogInformation( $"MyDependency2.WriteMessage Message: {message}");    }}

The updatedConfigureServices method registers the newIMyDependency implementation:

public void ConfigureServices(IServiceCollection services){    services.AddScoped<IMyDependency, MyDependency2>();    services.AddRazorPages();}

MyDependency2 depends onILogger<TCategoryName>, which it requests in the constructor.ILogger<TCategoryName> is aframework-provided service.

It's not unusual to use dependency injection 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 adependency tree,dependency graph, orobject graph.

The container resolvesILogger<TCategoryName> by taking advantage of(generic) open types, eliminating the need to register every(generic) constructed type.

In dependency injection terminology, a service:

  • Is typically an object that provides a service to other objects, such as theIMyDependency service.
  • Is not related to a web service, although the service may use a web service.

The framework provides a robustlogging system. TheIMyDependency implementations shown in the preceding examples were written to demonstrate basic DI, not to implement logging. Most apps shouldn't need to write loggers. The following code demonstrates using the default logging, which doesn't require any services to be registered inConfigureServices:

public class AboutModel : PageModel{    private readonly ILogger _logger;    public AboutModel(ILogger<AboutModel> logger)    {        _logger = logger;    }        public string Message { get; set; }    public void OnGet()    {        Message = $"About page visited at {DateTime.UtcNow.ToLongTimeString()}";        _logger.LogInformation(Message);    }}

Using the preceding code, there is no need to updateConfigureServices, becauselogging is provided by the framework.

Services injected into Startup

Services can be injected into theStartup constructor and theStartup.Configure method.

Only the following services can be injected into theStartup constructor when using the Generic Host (IHostBuilder):

Any service registered with the DI container can be injected into theStartup.Configure method:

public void Configure(IApplicationBuilder app, ILogger<Startup> logger){    ...}

For more information, seeApp startup in ASP.NET Core andAccess configuration in Startup.

Register groups of services with extension methods

The ASP.NET Core framework uses a convention for registering a group of related services. The convention is to use a singleAdd{GROUP_NAME} extension method to register all of the services required by a framework feature. For example, theAddControllers extension method registers the services required for MVC controllers.

The following code is generated by the Razor Pages template using individual user accounts and shows how to add additional services to the container using the extension methodsAddDbContext andAddDefaultIdentity:

public void ConfigureServices(IServiceCollection services){    services.AddDbContext<ApplicationDbContext>(options =>        options.UseSqlServer(            Configuration.GetConnectionString("DefaultConnection")));    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)        .AddEntityFrameworkStores<ApplicationDbContext>();    services.AddRazorPages();}

Consider the followingConfigureServices method, which registers services and configures options:

public void ConfigureServices(IServiceCollection services){    services.Configure<PositionOptions>(        Configuration.GetSection(PositionOptions.Position));    services.Configure<ColorOptions>(        Configuration.GetSection(ColorOptions.Color));    services.AddScoped<IMyDependency, MyDependency>();    services.AddScoped<IMyDependency2, MyDependency2>();    services.AddRazorPages();}

Related groups of registrations can be moved to an extension method to register services. For example, the configuration services are added to the following class:

using ConfigSample.Options;using Microsoft.Extensions.Configuration;namespace Microsoft.Extensions.DependencyInjection{    public static class MyConfigServiceCollectionExtensions    {        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 AddMyDependencyGroup(             this IServiceCollection services)        {            services.AddScoped<IMyDependency, MyDependency>();            services.AddScoped<IMyDependency2, MyDependency2>();            return services;        }    }}

The remaining services are registered in a similar class. The followingConfigureServices method uses the new extension methods to register the services:

public void ConfigureServices(IServiceCollection services){    services.AddConfig(Configuration)            .AddMyDependencyGroup();    services.AddRazorPages();}

Note: Eachservices.Add{GROUP_NAME} extension method adds and potentially configures services. For example,AddControllersWithViews adds the services MVC controllers with views require, andAddRazorPages adds the services Razor Pages requires. We recommend that apps follow the naming convention of creating extension methods in theMicrosoft.Extensions.DependencyInjection namespace. Creating extension methods in theMicrosoft.Extensions.DependencyInjection namespace:

  • Encapsulates groups of service registrations.
  • Provides convenientIntelliSense access to the service.

Service lifetimes

SeeService lifetimes inDependency injection in .NET

To use scoped services in middleware, use one of the following approaches:

  • Inject the service into the middleware'sInvoke orInvokeAsync method. Usingconstructor injection throws a runtime exception because it forces the scoped service to behave like a singleton. The sample in theLifetime and registration options section demonstrates theInvokeAsync approach.
  • UseFactory-based middleware. Middleware registered using this approach is activated per client request (connection), which allows scoped services to be injected into the middleware'sInvokeAsync method.

For more information, seeWrite custom ASP.NET Core middleware.

Service registration methods

SeeService registration methods inDependency injection in .NET

It's common to use multiple implementations whenmocking types for testing.

Registering a service with only an implementation type is equivalent to registering that service with the same implementation and service type. This is why multiple implementations of a service cannot be registered using the methods that don't take an explicit service type. These methods can register multipleinstances of a service, but they will all have the sameimplementation type.

Any of the above service registration methods can be used to register multiple service instances of the same service type. In the following example,AddSingleton is called twice withIMyDependency as the service type. The second call toAddSingleton overrides the previous one when resolved asIMyDependency and adds to the previous one when multiple services are resolved viaIEnumerable<IMyDependency>. Services appear in the order they were registered when resolved viaIEnumerable<{SERVICE}>.

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);    }}

Constructor injection behavior

SeeConstructor injection behavior inDependency injection in .NET

Entity Framework contexts

By default, Entity Framework contexts are added to the service container using thescoped lifetime because web app database operations are normally scoped to the client request. To use a different lifetime, specify the lifetime by using anAddDbContext 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:

public interface IOperation{    string OperationId { get; }}public interface IOperationTransient : IOperation { }public interface IOperationScoped : IOperation { }public interface IOperationSingleton : IOperation { }

The followingOperation class implements all of the preceding interfaces. TheOperation constructor generates a GUID and stores the last 4 characters in theOperationId property:

public class Operation : IOperationTransient, IOperationScoped, IOperationSingleton{    public Operation()    {        OperationId = Guid.NewGuid().ToString()[^4..];    }    public string OperationId { get; }}

TheStartup.ConfigureServices method creates multiple registrations of theOperation class according to the named lifetimes:

public void ConfigureServices(IServiceCollection services){    services.AddTransient<IOperationTransient, Operation>();    services.AddScoped<IOperationScoped, Operation>();    services.AddSingleton<IOperationSingleton, Operation>();    services.AddRazorPages();}

The sample app demonstrates object lifetimes both within and between requests. TheIndexModel and the middleware request each kind ofIOperation type and log theOperationId for each:

public class IndexModel : PageModel{    private readonly ILogger _logger;    private readonly IOperationTransient _transientOperation;    private readonly IOperationSingleton _singletonOperation;    private readonly IOperationScoped _scopedOperation;    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);    }}

Similar to theIndexModel, the middleware resolves the same services:

public class MyMiddleware{    private readonly RequestDelegate _next;    private readonly ILogger _logger;    private readonly IOperationTransient _transientOperation;    private readonly IOperationSingleton _singletonOperation;    public MyMiddleware(RequestDelegate next, ILogger<MyMiddleware> logger,        IOperationTransient transientOperation,        IOperationSingleton singletonOperation)    {        _logger = logger;        _transientOperation = transientOperation;        _singletonOperation = singletonOperation;        _next = next;    }    public async Task InvokeAsync(HttpContext context,        IOperationScoped scopedOperation)    {        _logger.LogInformation("Transient: " + _transientOperation.OperationId);        _logger.LogInformation("Scoped: "    + scopedOperation.OperationId);        _logger.LogInformation("Singleton: " + _singletonOperation.OperationId);        await _next(context);    }}public static class MyMiddlewareExtensions{    public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)    {        return builder.UseMiddleware<MyMiddleware>();    }}

Scoped services must be resolved in theInvokeAsync method:

public async Task InvokeAsync(HttpContext context,    IOperationScoped scopedOperation){    _logger.LogInformation("Transient: " + _transientOperation.OperationId);    _logger.LogInformation("Scoped: "    + scopedOperation.OperationId);    _logger.LogInformation("Singleton: " + _singletonOperation.OperationId);    await _next(context);}

The logger output shows:

  • Transient objects are always different. The transientOperationId value is different in theIndexModel and in the middleware.
  • Scoped objects are the same for a given request but differ across each new request.
  • Singleton objects are the same for every request.

To reduce the logging output, set "Logging:LogLevel:Microsoft:Error" in theappsettings.Development.json file:

{  "MyKey": "MyKey from appsettings.Development.json",  "Logging": {    "LogLevel": {      "Default": "Information",      "System": "Debug",      "Microsoft": "Error"    }  }}

Call services from main

Create anIServiceScope withIServiceScopeFactory.CreateScope to resolve a scoped service within the app's scope. This approach is useful to access a scoped service at startup to run initialization tasks.

The following example shows how to access the scopedIMyDependency service and call itsWriteMessage method inProgram.Main:

public class Program{    public static void Main(string[] args)    {        var host = CreateHostBuilder(args).Build();        using (var serviceScope = host.Services.CreateScope())        {            var services = serviceScope.ServiceProvider;            try            {                var myDependency = services.GetRequiredService<IMyDependency>();                myDependency.WriteMessage("Call services from main");            }            catch (Exception ex)            {                var logger = services.GetRequiredService<ILogger<Program>>();                logger.LogError(ex, "An error occurred.");            }        }        host.Run();    }    public static IHostBuilder CreateHostBuilder(string[] args) =>        Host.CreateDefaultBuilder(args)            .ConfigureWebHostDefaults(webBuilder =>            {                webBuilder.UseStartup<Startup>();            });}

Scope validation

SeeConstructor injection behavior inDependency injection in .NET

For more information, seeScope validation.

Request Services

Services and their dependencies within an ASP.NET Core request are exposed throughHttpContext.RequestServices.

The framework creates a scope per request, andRequestServices 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 fromRequestServices. Requesting dependencies as constructor parameters yields classes that are easier to test.

Design services for dependency injection

When designing services for dependency injection:

  • 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 a lot of injected dependencies, it might be a sign that the class has too many responsibilities and violates theSingle 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 callsDispose for theIDisposable 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:

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;    }}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;    }}public interface IService3{    public void Write(string message);}public class Service3 : IService3, IDisposable{    private bool _disposed;    public Service3(string myKey)    {        MyKey = myKey;    }    public string MyKey { get; }    public void Write(string message)    {        Console.WriteLine($"Service3: {message}, MyKey = {MyKey}");    }    public void Dispose()    {        if (_disposed)            return;        Console.WriteLine("Service3.Dispose");        _disposed = true;    }}
public void ConfigureServices(IServiceCollection services){    services.AddScoped<Service1>();    services.AddSingleton<Service2>();        var myKey = Configuration["MyKey"];    services.AddSingleton<IService3>(sp => new Service3(myKey));    services.AddRazorPages();}
public class IndexModel : PageModel{    private readonly Service1 _service1;    private readonly Service2 _service2;    private readonly IService3 _service3;    public IndexModel(Service1 service1, Service2 service2, IService3 service3)    {        _service1 = service1;        _service2 = service2;        _service3 = service3;    }    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.OnGetService2: IndexModel.OnGetService3: IndexModel.OnGet, MyKey = My Key from configService1.Dispose

Services not created by the service container

Consider the following code:

public void ConfigureServices(IServiceCollection services){    services.AddSingleton(new Service1());    services.AddSingleton(new Service2());    services.AddRazorPages();}

In 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 the services.

IDisposable guidance for Transient and shared instances

SeeIDisposable guidance for Transient and shared instance inDependency injection in .NET

Default service container replacement

SeeDefault service container replacement inDependency injection in .NET

Recommendations

SeeRecommendations inDependency injection in .NET

  • Avoid using theservice locator pattern. For example, don't invokeGetService to obtain a service instance when you can use DI instead:

    Incorrect:

    Incorrect code

    Correct:

    public class MyClass{    private readonly IOptionsMonitor<MyOptions> _optionsMonitor;    public MyClass(IOptionsMonitor<MyOptions> optionsMonitor)    {        _optionsMonitor = 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 mixInversion of Control strategies.

  • Avoid static access toHttpContext (for example,IHttpContextAccessor.HttpContext).

  • Avoid calls toBuildServiceProvider inConfigureServices. CallingBuildServiceProvider typically happens when the developer wants to resolve a service inConfigureServices. For example, consider the case where theLoginPath is loaded from configuration. Avoid the following approach:

    bad code calling BuildServiceProvider

    In the preceding image, selecting the green wavy line underservices.BuildServiceProvider shows the following ASP0000 warning:

    ASP0000 Calling 'BuildServiceProvider' from application code results in an additional copy of singleton services being created. Consider alternatives such as dependency injecting services as parameters to 'Configure'.

    CallingBuildServiceProvider creates a second container, which can create torn singletons and cause references to object graphs across multiple containers.

    A correct way to getLoginPath is to use the options pattern's built-in support for DI:

    public void ConfigureServices(IServiceCollection services){    services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)        .AddCookie();    services.AddOptions<CookieAuthenticationOptions>(                        CookieAuthenticationDefaults.AuthenticationScheme)        .Configure<IMyService>((options, myService) =>        {            options.LoginPath = myService.GetLoginPath();        });    services.AddRazorPages();}
  • Disposable transient services are captured by the container for disposal. This can turn into a memory leak if resolved from the top level container.

  • Enable scope validation to make sure the app doesn't have singletons that capture scoped services. For more information, seeScope validation.

Like all sets of recommendations, you may encounter situations where ignoring a recommendation is required. Exceptions are rare, mostly special cases within the framework itself.

DI is analternative to static/global object access patterns. You may not be able to realize the benefits of DI if you mix it with static object access.

Recommended patterns for multi-tenancy in DI

Orchard Core is an application framework for building modular, multi-tenant applications on ASP.NET Core. For more information, see theOrchard Core Documentation.

See theOrchard Core samples for examples of how to build modular and multi-tenant apps using just the Orchard Core Framework without any of its CMS-specific features.

Framework-provided services

TheStartup.ConfigureServices method registers services that the app uses, including platform features, such as Entity Framework Core and ASP.NET Core MVC. Initially, theIServiceCollection provided toConfigureServices has services defined by the framework depending onhow the host was configured. For apps based on the ASP.NET Core templates, the framework registers more than 250 services.

The following table lists a small sample of these framework-registered services:

Service TypeLifetime
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactoryTransient
IHostApplicationLifetimeSingleton
IWebHostEnvironmentSingleton
Microsoft.AspNetCore.Hosting.IStartupSingleton
Microsoft.AspNetCore.Hosting.IStartupFilterTransient
Microsoft.AspNetCore.Hosting.Server.IServerSingleton
Microsoft.AspNetCore.Http.IHttpContextFactoryTransient
Microsoft.Extensions.Logging.ILogger<TCategoryName>Singleton
Microsoft.Extensions.Logging.ILoggerFactorySingleton
Microsoft.Extensions.ObjectPool.ObjectPoolProviderSingleton
Microsoft.Extensions.Options.IConfigureOptions<TOptions>Transient
Microsoft.Extensions.Options.IOptions<TOptions>Singleton
System.Diagnostics.DiagnosticSourceSingleton
System.Diagnostics.DiagnosticListenerSingleton

Additional resources

Collaborate with us on GitHub
The source for this content can be found on GitHub, where you can also create and review issues and pull requests. For more information, seeour contributor guide.

Feedback

Was this page helpful?

YesNoNo

Need help with this topic?

Want to try using Ask Learn to clarify or guide you through this topic?

Suggest a fix?

  • Last updated on

In this article

Was this page helpful?

YesNo
NoNeed help with this topic?

Want to try using Ask Learn to clarify or guide you through this topic?

Suggest a fix?