"
 
 
 
ASP.NET (snapshot 2017) Microsoft documentation and samples

Introduction to Dependency Injection in ASP.NET Core

By Steve Smith and Scott Addie

ASP.NET Core is designed from the ground up to support and leverage dependency injection. ASP.NET Core applications can leverage built-in framework services by having them injected into methods in the Startup class, and application services can be configured for injection as well. The default services container provided by ASP.NET Core provides a minimal feature set and is not intended to replace other containers.

View or download sample code ((xref:)how to download)

What is Dependency Injection?

Dependency injection (DI) is a technique for achieving loose coupling between objects and their collaborators, or dependencies. Rather than directly instantiating collaborators, or using static references, the objects a class needs in order to perform its actions are provided to the class in some fashion. Most often, classes will declare their dependencies via their constructor, allowing them to follow the Explicit Dependencies Principle. This approach is known as “constructor injection”.

When classes are designed with DI in mind, they are more loosely coupled because they do not have direct, hard-coded dependencies on their collaborators. This follows the Dependency Inversion Principle, which states that “high level modules should not depend on low level modules; both should depend on abstractions.” Instead of referencing specific implementations, classes request abstractions (typically interfaces) which are provided to them when the class is constructed. Extracting dependencies into interfaces and providing implementations of these interfaces as parameters is also an example of the Strategy design pattern.

When a system is designed to use DI, with many classes requesting their dependencies via their constructor (or properties), it’s helpful to have a class dedicated to creating these classes with their associated dependencies. These classes are referred to as containers, or more specifically, Inversion of Control (IoC) containers or Dependency Injection (DI) containers. A container is essentially a factory that is responsible for providing instances of types that are requested from it. If a given type has declared that it has dependencies, and the container has been configured to provide the dependency types, it will create the dependencies as part of creating the requested instance. In this way, complex dependency graphs can be provided to classes without the need for any hard-coded object construction. In addition to creating objects with their dependencies, containers typically manage object lifetimes within the application.

ASP.NET Core includes a simple built-in container (represented by the IServiceProvider interface) that supports constructor injection by default, and ASP.NET makes certain services available through DI. ASP.NET’s container refers to the types it manages as services. Throughout the rest of this article, services will refer to types that are managed by ASP.NET Core’s IoC container. You configure the built-in container’s services in the ConfigureServices method in your application’s Startup class.

[!NOTE] Martin Fowler has written an extensive article on Inversion of Control Containers and the Dependency Injection Pattern. Microsoft Patterns and Practices also has a great description of Dependency Injection.

[!NOTE] This article covers Dependency Injection as it applies to all ASP.NET applications. Dependency Injection within MVC controllers is covered in Dependency Injection and Controllers.

Constructor Injection Behavior

Constructor injection requires that the constructor in question be public. Otherwise, your app will throw an InvalidOperationException:

A suitable constructor for type ‘YourType’ could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.

Constructor injection requires that only one applicable constructor exist. Constructor overloads are supported, but only one overload can exist whose arguments can all be fulfilled by dependency injection. If more than one exists, your app will throw an InvalidOperationException:

Multiple constructors accepting all given argument types have been found in type ‘YourType’. There should only be one applicable constructor.

Constructors can accept arguments that are not provided by dependency injection, but these must support default values. For example:

Using Framework-Provided Services

The ConfigureServices method in the Startup class is responsible for defining the services the application will use, including platform features like Entity Framework Core and ASP.NET Core MVC. Initially, the IServiceCollection provided to ConfigureServices has the following services defined (depending on (xref:)how the host was configured):

Service Type Lifetime
Microsoft.AspNetCore.Hosting.IHostingEnvironment Singleton
Microsoft.Extensions.Logging.ILoggerFactory Singleton
Microsoft.Extensions.Logging.ILogger<T> Singleton
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory Transient
Microsoft.AspNetCore.Http.IHttpContextFactory Transient
Microsoft.Extensions.Options.IOptions<T> Singleton
System.Diagnostics.DiagnosticSource Singleton
System.Diagnostics.DiagnosticListener Singleton
Microsoft.AspNetCore.Hosting.IStartupFilter Transient
Microsoft.Extensions.ObjectPool.ObjectPoolProvider Singleton
Microsoft.Extensions.Options.IConfigureOptions<T> Transient
Microsoft.AspNetCore.Hosting.Server.IServer Singleton
Microsoft.AspNetCore.Hosting.IStartup Singleton
Microsoft.AspNetCore.Hosting.IApplicationLifetime Singleton

Below is an example of how to add additional services to the container using a number of extension methods like AddDbContext, AddIdentity, and AddMvc.

[!code-csharpMain]

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Threading.Tasks;
   5:  using Microsoft.AspNetCore.Builder;
   6:  using Microsoft.AspNetCore.Hosting;
   7:  using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
   8:  using Microsoft.EntityFrameworkCore;
   9:  using Microsoft.Extensions.Configuration;
  10:  using Microsoft.Extensions.DependencyInjection;
  11:  using Microsoft.Extensions.Logging;
  12:  using WebApplication1.Data;
  13:  using WebApplication1.Models;
  14:  using WebApplication1.Services;
  15:   
  16:  namespace WebApplication1
  17:  {
  18:      public class Startup
  19:      {
  20:          public Startup(IHostingEnvironment env)
  21:          {
  22:              var builder = new ConfigurationBuilder()
  23:                  .SetBasePath(env.ContentRootPath)
  24:                  .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
  25:                  .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
  26:   
  27:              if (env.IsDevelopment())
  28:              {
  29:                  // For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709
  30:                  builder.AddUserSecrets<Startup>();
  31:              }
  32:   
  33:              builder.AddEnvironmentVariables();
  34:              Configuration = builder.Build();
  35:          }
  36:   
  37:          public IConfigurationRoot Configuration { get; }
  38:   
  39:          // This method gets called by the runtime. Use this method to add services to the container.
  40:          public void ConfigureServices(IServiceCollection services)
  41:          {
  42:              // Add framework services.
  43:              services.AddDbContext<ApplicationDbContext>(options =>
  44:                  options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
  45:   
  46:              services.AddIdentity<ApplicationUser, IdentityRole>()
  47:                  .AddEntityFrameworkStores<ApplicationDbContext>()
  48:                  .AddDefaultTokenProviders();
  49:   
  50:              services.AddMvc();
  51:   
  52:              // Add application services.
  53:              services.AddTransient<IEmailSender, AuthMessageSender>();
  54:              services.AddTransient<ISmsSender, AuthMessageSender>();
  55:          }
  56:   
  57:          // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
  58:          public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
  59:          {
  60:              loggerFactory.AddConsole(Configuration.GetSection("Logging"));
  61:              loggerFactory.AddDebug();
  62:   
  63:              if (env.IsDevelopment())
  64:              {
  65:                  app.UseDeveloperExceptionPage();
  66:                  app.UseDatabaseErrorPage();
  67:                  app.UseBrowserLink();
  68:              }
  69:              else
  70:              {
  71:                  app.UseExceptionHandler("/Home/Error");
  72:              }
  73:   
  74:              app.UseStaticFiles();
  75:   
  76:              app.UseIdentity();
  77:   
  78:              app.UseMvc(routes =>
  79:              {
  80:                  routes.MapRoute(
  81:                      name: "default",
  82:                      template: "{controller=Home}/{action=Index}/{id?}");
  83:              });
  84:          }
  85:      }
  86:  }

The features and middleware provided by ASP.NET, such as MVC, follow a convention of using a single AddServiceName extension method to register all of the services required by that feature.

[!TIP] You can request certain framework-provided services within Startup methods through their parameter lists - see Application Startup for more details.

Registering Your Own Services

You can register your own application services as follows. The first generic type represents the type (typically an interface) that will be requested from the container. The second generic type represents the concrete type that will be instantiated by the container and used to fulfill such requests.

[!code-csharpMain]

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Threading.Tasks;
   5:  using Microsoft.AspNetCore.Builder;
   6:  using Microsoft.AspNetCore.Hosting;
   7:  using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
   8:  using Microsoft.EntityFrameworkCore;
   9:  using Microsoft.Extensions.Configuration;
  10:  using Microsoft.Extensions.DependencyInjection;
  11:  using Microsoft.Extensions.Logging;
  12:  using WebApplication1.Data;
  13:  using WebApplication1.Models;
  14:  using WebApplication1.Services;
  15:   
  16:  namespace WebApplication1
  17:  {
  18:      public class Startup
  19:      {
  20:          public Startup(IHostingEnvironment env)
  21:          {
  22:              var builder = new ConfigurationBuilder()
  23:                  .SetBasePath(env.ContentRootPath)
  24:                  .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
  25:                  .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
  26:   
  27:              if (env.IsDevelopment())
  28:              {
  29:                  // For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709
  30:                  builder.AddUserSecrets<Startup>();
  31:              }
  32:   
  33:              builder.AddEnvironmentVariables();
  34:              Configuration = builder.Build();
  35:          }
  36:   
  37:          public IConfigurationRoot Configuration { get; }
  38:   
  39:          // This method gets called by the runtime. Use this method to add services to the container.
  40:          public void ConfigureServices(IServiceCollection services)
  41:          {
  42:              // Add framework services.
  43:              services.AddDbContext<ApplicationDbContext>(options =>
  44:                  options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
  45:   
  46:              services.AddIdentity<ApplicationUser, IdentityRole>()
  47:                  .AddEntityFrameworkStores<ApplicationDbContext>()
  48:                  .AddDefaultTokenProviders();
  49:   
  50:              services.AddMvc();
  51:   
  52:              // Add application services.
  53:              services.AddTransient<IEmailSender, AuthMessageSender>();
  54:              services.AddTransient<ISmsSender, AuthMessageSender>();
  55:          }
  56:   
  57:          // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
  58:          public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
  59:          {
  60:              loggerFactory.AddConsole(Configuration.GetSection("Logging"));
  61:              loggerFactory.AddDebug();
  62:   
  63:              if (env.IsDevelopment())
  64:              {
  65:                  app.UseDeveloperExceptionPage();
  66:                  app.UseDatabaseErrorPage();
  67:                  app.UseBrowserLink();
  68:              }
  69:              else
  70:              {
  71:                  app.UseExceptionHandler("/Home/Error");
  72:              }
  73:   
  74:              app.UseStaticFiles();
  75:   
  76:              app.UseIdentity();
  77:   
  78:              app.UseMvc(routes =>
  79:              {
  80:                  routes.MapRoute(
  81:                      name: "default",
  82:                      template: "{controller=Home}/{action=Index}/{id?}");
  83:              });
  84:          }
  85:      }
  86:  }

[!NOTE] Each services.Add<ServiceName> extension method adds (and potentially configures) services. For example, services.AddMvc() adds the services MVC requires. It’s recommended that you follow this convention, placing extension methods in the Microsoft.Extensions.DependencyInjection namespace, to encapsulate groups of service registrations.

The AddTransient method is used to map abstract types to concrete services that are instantiated separately for every object that requires it. This is known as the service’s lifetime, and additional lifetime options are described below. It is important to choose an appropriate lifetime for each of the services you register. Should a new instance of the service be provided to each class that requests it? Should one instance be used throughout a given web request? Or should a single instance be used for the lifetime of the application?

In the sample for this article, there is a simple controller that displays character names, called CharactersController. Its Index method displays the current list of characters that have been stored in the application, and initializes the collection with a handful of characters if none exist. Note that although this application uses Entity Framework Core and the ApplicationDbContext class for its persistence, none of that is apparent in the controller. Instead, the specific data access mechanism has been abstracted behind an interface, ICharacterRepository, which follows the repository pattern. An instance of ICharacterRepository is requested via the constructor and assigned to a private field, which is then used to access characters as necessary.

[!code-csharpMain]

   1:  using System.Linq;
   2:  using DependencyInjectionSample.Interfaces;
   3:  using DependencyInjectionSample.Models;
   4:  using Microsoft.AspNetCore.Mvc;
   5:   
   6:  namespace DependencyInjectionSample.Controllers
   7:  {
   8:      public class CharactersController : Controller
   9:      {
  10:          private readonly ICharacterRepository _characterRepository;
  11:   
  12:          public CharactersController(ICharacterRepository characterRepository)
  13:          {
  14:              _characterRepository = characterRepository;
  15:          }
  16:   
  17:          // GET: /characters/
  18:          public IActionResult Index()
  19:          {
  20:              PopulateCharactersIfNoneExist();
  21:              var characters = _characterRepository.ListAll();
  22:   
  23:              return View(characters);
  24:          }
  25:          
  26:          private void PopulateCharactersIfNoneExist()
  27:          {
  28:              if (!_characterRepository.ListAll().Any())
  29:              {
  30:                  _characterRepository.Add(new Character("Darth Maul"));
  31:                  _characterRepository.Add(new Character("Darth Vader"));
  32:                  _characterRepository.Add(new Character("Yoda"));
  33:                  _characterRepository.Add(new Character("Mace Windu"));
  34:              }
  35:          }
  36:      }
  37:  }

The ICharacterRepository defines the two methods the controller needs to work with Character instances.

[!code-csharpMain]

   1:  using System.Collections.Generic;
   2:  using DependencyInjectionSample.Models;
   3:   
   4:  namespace DependencyInjectionSample.Interfaces
   5:  {
   6:      public interface ICharacterRepository
   7:      {
   8:          IEnumerable<Character> ListAll();
   9:          void Add(Character character);
  10:      }
  11:  }

This interface is in turn implemented by a concrete type, CharacterRepository, that is used at runtime.

[!NOTE] The way DI is used with the CharacterRepository class is a general model you can follow for all of your application services, not just in “repositories” or data access classes.

[!code-csharpMain]

   1:  using System.Collections.Generic;
   2:  using System.Linq;
   3:  using DependencyInjectionSample.Interfaces;
   4:   
   5:  namespace DependencyInjectionSample.Models
   6:  {
   7:      public class CharacterRepository : ICharacterRepository
   8:      {
   9:          private readonly ApplicationDbContext _dbContext;
  10:   
  11:          public CharacterRepository(ApplicationDbContext dbContext)
  12:          {
  13:              _dbContext = dbContext;
  14:          }
  15:   
  16:          public IEnumerable<Character> ListAll()
  17:          {
  18:              return _dbContext.Characters.AsEnumerable();
  19:          }
  20:   
  21:          public void Add(Character character)
  22:          {
  23:              _dbContext.Characters.Add(character);
  24:              _dbContext.SaveChanges();
  25:          }
  26:      }
  27:  }

Note that CharacterRepository requests an ApplicationDbContext in its constructor. It is not unusual for dependency injection to be used in a chained fashion like this, with each requested dependency in turn requesting its own dependencies. The container is responsible for resolving all of the dependencies in the graph and returning the fully resolved service.

[!NOTE] Creating the requested object, and all of the objects it requires, and all of the objects those require, is sometimes referred to as an object graph. Likewise, the collective set of dependencies that must be resolved is typically referred to as a dependency tree or dependency graph.

In this case, both ICharacterRepository and in turn ApplicationDbContext must be registered with the services container in ConfigureServices in Startup. ApplicationDbContext is configured with the call to the extension method AddDbContext<T>. The following code shows the registration of the CharacterRepository type.

[!code-csharpMain]

   1:  using System;
   2:  using DependencyInjectionSample.Interfaces;
   3:  using DependencyInjectionSample.Models;
   4:  using DependencyInjectionSample.Services;
   5:  using Microsoft.AspNetCore.Builder;
   6:  using Microsoft.AspNetCore.Hosting;
   7:  using Microsoft.EntityFrameworkCore;
   8:  using Microsoft.Extensions.DependencyInjection;
   9:   
  10:   
  11:  namespace DependencyInjectionSample
  12:  {
  13:      public class Startup
  14:      {
  15:          // This method gets called by the runtime. Use this method to add services to the container.
  16:          public void ConfigureServices(IServiceCollection services)
  17:          {
  18:              services.AddDbContext<ApplicationDbContext>(options =>
  19:                  options.UseInMemoryDatabase()
  20:              );
  21:   
  22:              // Add framework services.
  23:              services.AddMvc();
  24:   
  25:              // Register application services.
  26:              services.AddScoped<ICharacterRepository, CharacterRepository>();
  27:              services.AddTransient<IOperationTransient, Operation>();
  28:              services.AddScoped<IOperationScoped, Operation>();
  29:              services.AddSingleton<IOperationSingleton, Operation>();
  30:              services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));
  31:              services.AddTransient<OperationService, OperationService>();
  32:          }
  33:   
  34:          // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
  35:          public void Configure(IApplicationBuilder app, IHostingEnvironment env)
  36:          {
  37:              if (env.IsDevelopment())
  38:              {
  39:                  app.UseDeveloperExceptionPage();
  40:              }
  41:   
  42:              app.UseStaticFiles();
  43:   
  44:              app.UseMvcWithDefaultRoute();
  45:          }
  46:      }
  47:  }

Entity Framework contexts should be added to the services container using the Scoped lifetime. This is taken care of automatically if you use the helper methods as shown above. Repositories that will make use of Entity Framework should use the same lifetime.

[!WARNING] The main danger to be wary of is resolving a Scoped service from a singleton. It’s likely in such a case that the service will have incorrect state when processing subsequent requests.

Services that have dependencies should register them in the container. If a service’s constructor requires a primitive, such as a string, this can be injected by using (xref:)configuration and the (xref:)options pattern.

Service Lifetimes and Registration Options

ASP.NET services can be configured with the following lifetimes:

Transient

Transient lifetime services are created each time they are requested. This lifetime works best for lightweight, stateless services.

Scoped

Scoped lifetime services are created once per request.

Singleton

Singleton lifetime services are created the first time they are requested (or when ConfigureServices is run if you specify an instance there) and then every subsequent request will use the same instance. If your application requires singleton behavior, allowing the services container to manage the service’s lifetime is recommended instead of implementing the singleton design pattern and managing your object’s lifetime in the class yourself.

Services can be registered with the container in several ways. We have already seen how to register a service implementation with a given type by specifying the concrete type to use. In addition, a factory can be specified, which will then be used to create the instance on demand. The third approach is to directly specify the instance of the type to use, in which case the container will never attempt to create an instance (nor will it dispose of the instance).

To demonstrate the difference between these lifetime and registration options, consider a simple interface that represents one or more tasks as an operation with a unique identifier, OperationId. Depending on how we configure the lifetime for this service, the container will provide either the same or different instances of the service to the requesting class. To make it clear which lifetime is being requested, we will create one type per lifetime option:

[!code-csharpMain]

   1:  using System;
   2:   
   3:  namespace DependencyInjectionSample.Interfaces
   4:  {
   5:      public interface IOperation
   6:      {
   7:          Guid OperationId { get; }
   8:      }
   9:   
  10:      public interface IOperationTransient : IOperation
  11:      {
  12:      }
  13:      public interface IOperationScoped : IOperation
  14:      {
  15:      }
  16:      public interface IOperationSingleton : IOperation
  17:      {
  18:      }
  19:      public interface IOperationSingletonInstance : IOperation
  20:      {
  21:      }
  22:  }

We implement these interfaces using a single class, Operation, that accepts a Guid in its constructor, or uses a new Guid if none is provided.

Next, in ConfigureServices, each type is added to the container according to its named lifetime:

[!code-csharpMain]

   1:  using System;
   2:  using DependencyInjectionSample.Interfaces;
   3:  using DependencyInjectionSample.Models;
   4:  using DependencyInjectionSample.Services;
   5:  using Microsoft.AspNetCore.Builder;
   6:  using Microsoft.AspNetCore.Hosting;
   7:  using Microsoft.EntityFrameworkCore;
   8:  using Microsoft.Extensions.DependencyInjection;
   9:   
  10:   
  11:  namespace DependencyInjectionSample
  12:  {
  13:      public class Startup
  14:      {
  15:          // This method gets called by the runtime. Use this method to add services to the container.
  16:          public void ConfigureServices(IServiceCollection services)
  17:          {
  18:              services.AddDbContext<ApplicationDbContext>(options =>
  19:                  options.UseInMemoryDatabase()
  20:              );
  21:   
  22:              // Add framework services.
  23:              services.AddMvc();
  24:   
  25:              // Register application services.
  26:              services.AddScoped<ICharacterRepository, CharacterRepository>();
  27:              services.AddTransient<IOperationTransient, Operation>();
  28:              services.AddScoped<IOperationScoped, Operation>();
  29:              services.AddSingleton<IOperationSingleton, Operation>();
  30:              services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));
  31:              services.AddTransient<OperationService, OperationService>();
  32:          }
  33:   
  34:          // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
  35:          public void Configure(IApplicationBuilder app, IHostingEnvironment env)
  36:          {
  37:              if (env.IsDevelopment())
  38:              {
  39:                  app.UseDeveloperExceptionPage();
  40:              }
  41:   
  42:              app.UseStaticFiles();
  43:   
  44:              app.UseMvcWithDefaultRoute();
  45:          }
  46:      }
  47:  }

Note that the IOperationSingletonInstance service is using a specific instance with a known ID of Guid.Empty so it will be clear when this type is in use (its Guid will be all zeroes). We have also registered an OperationService that depends on each of the other Operation types, so that it will be clear within a request whether this service is getting the same instance as the controller, or a new one, for each operation type. All this service does is expose its dependencies as properties, so they can be displayed in the view.

[!code-csharpMain]

   1:  using DependencyInjectionSample.Interfaces;
   2:   
   3:  namespace DependencyInjectionSample.Services
   4:  {
   5:      public class OperationService
   6:      {
   7:          public IOperationTransient TransientOperation { get; }
   8:          public IOperationScoped ScopedOperation { get; }
   9:          public IOperationSingleton SingletonOperation { get; }
  10:          public IOperationSingletonInstance SingletonInstanceOperation { get; }
  11:   
  12:          public OperationService(IOperationTransient transientOperation,
  13:              IOperationScoped scopedOperation,
  14:              IOperationSingleton singletonOperation,
  15:              IOperationSingletonInstance instanceOperation)
  16:          {
  17:              TransientOperation = transientOperation;
  18:              ScopedOperation = scopedOperation;
  19:              SingletonOperation = singletonOperation;
  20:              SingletonInstanceOperation = instanceOperation;
  21:          }
  22:      }
  23:  }

To demonstrate the object lifetimes within and between separate individual requests to the application, the sample includes an OperationsController that requests each kind of IOperation type as well as an OperationService. The Index action then displays all of the controller’s and service’s OperationId values.

[!code-csharpMain]

   1:  using DependencyInjectionSample.Interfaces;
   2:  using DependencyInjectionSample.Services;
   3:  using Microsoft.AspNetCore.Mvc;
   4:   
   5:  namespace DependencyInjectionSample.Controllers
   6:  {
   7:      public class OperationsController : Controller
   8:      {
   9:          private readonly OperationService _operationService;
  10:          private readonly IOperationTransient _transientOperation;
  11:          private readonly IOperationScoped _scopedOperation;
  12:          private readonly IOperationSingleton _singletonOperation;
  13:          private readonly IOperationSingletonInstance _singletonInstanceOperation;
  14:   
  15:          public OperationsController(OperationService operationService,
  16:              IOperationTransient transientOperation,
  17:              IOperationScoped scopedOperation,
  18:              IOperationSingleton singletonOperation,
  19:              IOperationSingletonInstance singletonInstanceOperation)
  20:          {
  21:              _operationService = operationService;
  22:              _transientOperation = transientOperation;
  23:              _scopedOperation = scopedOperation;
  24:              _singletonOperation = singletonOperation;
  25:              _singletonInstanceOperation = singletonInstanceOperation;
  26:          }
  27:   
  28:          public IActionResult Index()
  29:          {
  30:              // viewbag contains controller-requested services
  31:              ViewBag.Transient = _transientOperation;
  32:              ViewBag.Scoped = _scopedOperation;
  33:              ViewBag.Singleton = _singletonOperation;
  34:              ViewBag.SingletonInstance = _singletonInstanceOperation;
  35:              
  36:              // operation service has its own requested services
  37:              ViewBag.Service = _operationService;
  38:              return View();
  39:          }
  40:      }
  41:  }

Now two separate requests are made to this controller action:

The Operations view of the Dependency Injection Sample web application running in Microsoft Edge showing Operation ID values (GUID’s) for Transient, Scoped, Singleton, and Instance Controller and Operation Service Operations on the first request.
The Operations view of the Dependency Injection Sample web application running in Microsoft Edge showing Operation ID values (GUID’s) for Transient, Scoped, Singleton, and Instance Controller and Operation Service Operations on the first request.
The operations view showing the Operation ID values for a second request.
The operations view showing the Operation ID values for a second request.

Observe which of the OperationId values vary within a request, and between requests.

Request Services

The services available within an ASP.NET request from HttpContext are exposed through the RequestServices collection.

HttpContext Request Services Intellisense contextual dialog stating that Request Services gets or sets the IServiceProvider that provides access to the request’s service container.
HttpContext Request Services Intellisense contextual dialog stating that Request Services gets or sets the IServiceProvider that provides access to the request’s service container.

Request Services represent the services you configure and request as part of your application. When your objects specify dependencies, these are satisfied by the types found in RequestServices, not ApplicationServices.

Generally, you shouldn’t use these properties directly, preferring instead to request the types your classes you require via your class’s constructor, and letting the framework inject these dependencies. This yields classes that are easier to test (see Testing) and are more loosely coupled.

[!NOTE] Prefer requesting dependencies as constructor parameters to accessing the RequestServices collection.

Designing Your Services For Dependency Injection

You should design your services to use dependency injection to get their collaborators. This means avoiding the use of stateful static method calls (which result in a code smell known as static cling) and the direct instantiation of dependent classes within your services. It may help to remember the phrase, New is Glue, when choosing whether to instantiate a type or to request it via dependency injection. By following the SOLID Principles of Object Oriented Design, your classes will naturally tend to be small, well-factored, and easily tested.

What if you find that your classes tend to have way too many dependencies being injected? This is generally a sign that your class is trying to do too much, and is probably violating SRP - the Single Responsibility Principle. See if you can refactor the class by moving some of its responsibilities into a new class. Keep in mind that your Controller classes should be focused on UI concerns, so business rules and data access implementation details should be kept in classes appropriate to these separate concerns.

With regards to data access specifically, you can inject the DbContext into your controllers (assuming you’ve added EF to the services container in ConfigureServices). Some developers prefer to use a repository interface to the database rather than injecting the DbContext directly. Using an interface to encapsulate the data access logic in one place can minimize how many places you will have to change when your database changes.

Disposing of services

The container will call Dispose for IDisposable types it creates. However, if you add an instance to the container yourself, it will not be disposed.

Example:

[!NOTE] In version 1.0, the container called dispose on all IDisposable objects, including those it did not create.

Replacing the default services container

The built-in services container is meant to serve the basic needs of the framework and most consumer applications built on it. However, developers can replace the built-in container with their preferred container. The ConfigureServices method typically returns void, but if its signature is changed to return IServiceProvider, a different container can be configured and returned. There are many IOC containers available for .NET. In this example, the Autofac package is used.

First, install the appropriate container package(s):

Next, configure the container in ConfigureServices and return an IServiceProvider:

[!NOTE] When using a third-party DI container, you must change ConfigureServices so that it returns IServiceProvider instead of void.

Finally, configure Autofac as normal in DefaultModule:

At runtime, Autofac will be used to resolve types and inject dependencies. Learn more about using Autofac and ASP.NET Core.

Thread safety

Singleton services need to be thread safe. If a singleton service has a dependency on a transient service, the transient service may also need to be thread safe depending how it’s used by the singleton.

Recommendations

When working with dependency injection, keep the following recommendations in mind:

[!NOTE] Like all sets of recommendations, you may encounter situations where ignoring one is required. We have found exceptions to be rare – mostly very special cases within the framework itself.

Remember, dependency injection is an alternative to static/global object access patterns. You will not be able to realize the benefits of DI if you mix it with static object access.

Additional Resources





Comments ( )
<00>  <01>  <02>  <03>  <04>  <05>  <06>  <07>  <08>  <09>  <10>  <11>  <12>  <13>  <14>  <15>  <16>  <17>  <18>  <19>  <20>  <21>  <22>  <23
Link to this page: //www.vb-net.com/AspNet-DocAndSamples-2017/aspnetcore/fundamentals/dependency-injection.htm
<SITEMAP>  <MVC>  <ASP>  <NET>  <DATA>  <KIOSK>  <FLEX>  <SQL>  <NOTES>  <LINUX>  <MONO>  <FREEWARE>  <DOCS>  <ENG>  <CHAT ME>  <ABOUT ME>  < THANKS ME>