Injection de dépendances dans ASP.NET CoreDependency injection in ASP.NET Core

Par Steve Smith, Scott Addie et Luke LathamBy Steve Smith, Scott Addie, and Luke Latham

ASP.NET Core prend en charge le modèle de conception de logiciel avec injection de dépendances (DI), une technique permettant d’obtenir une inversion de contrôle (IoC) entre les classes et leurs dépendances.ASP.NET Core supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies.

Pour plus d’informations spécifiques à l’injection de dépendances au sein des contrôleurs MVC, consultez Injection de dépendances dans les contrôleurs dans ASP.NET Core.For more information specific to dependency injection within MVC controllers, see Injection de dépendances dans les contrôleurs dans ASP.NET Core.

Affichez ou téléchargez l’exemple de code (procédure de téléchargement)View or download sample code (how to download)

Vue d’ensemble de l’injection de dépendancesOverview of dependency injection

Une dépendance est un objet qui nécessite un autre objet.A dependency is any object that another object requires. Examinez la classe MyDependency suivante avec une méthode WriteMessage dont dépendent d’autres classes dans une application :Examine the following MyDependency class with a WriteMessage method that other classes in an app depend upon:

public class MyDependency
{
    public MyDependency()
    {
    }

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

        return Task.FromResult(0);
    }
}

Une instance de la classe MyDependency peut être créée pour rendre la méthode WriteMessage disponible pour une classe.An instance of the MyDependency class can be created to make the WriteMessage method available to a class. La classe MyDependency est une dépendance de la classe IndexModel :The MyDependency class is a dependency of the IndexModel class:

public class IndexModel : PageModel
{
    MyDependency _dependency = new MyDependency();

    public async Task OnGetAsync()
    {
        await _dependency.WriteMessage(
            "IndexModel.OnGetAsync created this message.");
    }
}

Une instance de la classe MyDependency peut être créée pour rendre la méthode WriteMessage disponible pour une classe.An instance of the MyDependency class can be created to make the WriteMessage method available to a class. La classe MyDependency est une dépendance de la classe HomeController :The MyDependency class is a dependency of the HomeController class:

public class HomeController : Controller
{
    MyDependency _dependency = new MyDependency();

    public async Task<IActionResult> Index()
    {
        await _dependency.WriteMessage(
            "HomeController.Index created this message.");

        return View();
    }
}

La classe est créee et dépend directement de l’instance MyDependency.The class creates and directly depends on the MyDependency instance. Les dépendances de code (comme l’exemple précédent) posent problème et doivent être évitées pour les raisons suivantes :Code dependencies (such as the previous example) are problematic and should be avoided for the following reasons:

  • Pour remplacer MyDependency par une autre implémentation, la classe doit être modifiée.To replace MyDependency with a different implementation, the class must be modified.
  • Si MyDependency possède des dépendances, elles doivent être configurées par la classe.If MyDependency has dependencies, they must be configured by the class. Dans un grand projet comportant plusieurs classes dépendant de MyDependency, le code de configuration est disséminé dans l’application.In a large project with multiple classes depending on MyDependency, the configuration code becomes scattered across the app.
  • Cette implémentation complique le test unitaire.This implementation is difficult to unit test. L’application doit utiliser une classe MyDependency fictive ou stub, ce qui est impossible avec cette approche.The app should use a mock or stub MyDependency class, which isn't possible with this approach.

L’injection de dépendances résout ces problèmes via :Dependency injection addresses these problems through:

  • L’utilisation d’une interface pour abstraire l’implémentation des dépendances.The use of an interface to abstract the dependency implementation.
  • L’inscription de la dépendance dans un conteneur de service.Registration of the dependency in a service container. ASP.NET Core fournit un conteneur de service intégré, IServiceProvider.ASP.NET Core provides a built-in service container, IServiceProvider. Les services sont inscrits dans la méthode Startup.ConfigureServices de l’application.Services are registered in the app's Startup.ConfigureServices method.
  • Injection du service dans le constructeur de la classe où il est utilisé.Injection of the service into the constructor of the class where it's used. Le framework prend la responsabilité de la création d’une instance de la dépendance et de sa suppression lorsqu’elle n’est plus nécessaire.The framework takes on the responsibility of creating an instance of the dependency and disposing of it when it's no longer needed.

Dans l’exemple d’application, l’interface IMyDependency définit une méthode que le service fournit à l’application :In the sample app, the IMyDependency interface defines a method that the service provides to the app:

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

Cette interface est implémentée par un type concret, MyDependency :This interface is implemented by a concrete type, MyDependency:

public class MyDependency : IMyDependency
{
    private readonly ILogger<MyDependency> _logger;

    public MyDependency(ILogger<MyDependency> logger)
    {
        _logger = logger;
    }

    public Task WriteMessage(string message)
    {
        _logger.LogInformation(
            "MyDependency.WriteMessage called. Message: {MESSAGE}", 
            message);

        return Task.FromResult(0);
    }
}
public class MyDependency : IMyDependency
{
    private readonly ILogger<MyDependency> _logger;

    public MyDependency(ILogger<MyDependency> logger)
    {
        _logger = logger;
    }

    public Task WriteMessage(string message)
    {
        _logger.LogInformation(
            "MyDependency.WriteMessage called. Message: {MESSAGE}", 
            message);

        return Task.FromResult(0);
    }
}

MyDependency demande un ILogger<TCategoryName> dans son constructeur.MyDependency requests an ILogger<TCategoryName> in its constructor. Il n’est pas rare que l’injection de dépendances soit utilisée de manière chaînée.It's not unusual to use dependency injection in a chained fashion. Dans ce cas, chaque dépendance demandée demande à son tour ses propres dépendances.Each requested dependency in turn requests its own dependencies. Le conteneur résout les dépendances dans le graphique et retourne le service entièrement résolu.The container resolves the dependencies in the graph and returns the fully resolved service. L’ensemble collectif de dépendances qui doivent être résolues est généralement appelé arborescence des dépendances, graphique de dépendance ou graphique d’objet.The collective set of dependencies that must be resolved is typically referred to as a dependency tree, dependency graph, or object graph.

IMyDependency et ILogger<TCategoryName> doivent être inscrits dans le conteneur de service.IMyDependency and ILogger<TCategoryName> must be registered in the service container. IMyDependency est inscrit dans Startup.ConfigureServices.IMyDependency is registered in Startup.ConfigureServices. ILogger<TCategoryName> est inscrit par l’infrastructure d’abstractions de journalisation. Il s’agit donc d’un service fourni par le framework et inscrit par défaut par l’infrastructure.ILogger<TCategoryName> is registered by the logging abstractions infrastructure, so it's a framework-provided service registered by default by the framework.

Dans l’exemple d’application, le service IMyDependency est inscrit avec le type concret MyDependency.In the sample app, the IMyDependency service is registered with the concrete type MyDependency. L’inscription ajuste la durée de vie du service à la durée de vie d’une requête unique.The registration scopes the service lifetime to the lifetime of a single request. Les durées de vie du service sont décrites plus loin dans cette rubrique.Service lifetimes are described later in this topic.

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    services.AddScoped<IMyDependency, MyDependency>();
    services.AddTransient<IOperationTransient, Operation>();
    services.AddScoped<IOperationScoped, Operation>();
    services.AddSingleton<IOperationSingleton, Operation>();
    services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));

    // OperationService depends on each of the other Operation types.
    services.AddTransient<OperationService, OperationService>();
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddScoped<IMyDependency, MyDependency>();
    services.AddTransient<IOperationTransient, Operation>();
    services.AddScoped<IOperationScoped, Operation>();
    services.AddSingleton<IOperationSingleton, Operation>();
    services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));

    // OperationService depends on each of the other Operation types.
    services.AddTransient<OperationService, OperationService>();
}

Note

Chaque méthode d’extension services.Add{SERVICE_NAME} ajoute (et éventuellement configure) des services.Each services.Add{SERVICE_NAME} extension method adds (and potentially configures) services. Par exemple, services.AddMvc() ajoute les services dont Razor Pages et MVC ont besoin.For example, services.AddMvc() adds the services Razor Pages and MVC require. Il est recommandé que les applications suivent cette convention.We recommended that apps follow this convention. Placez les méthodes d’extension dans l’espace de noms Microsoft.Extensions.DependencyInjection pour encapsuler des groupes d’inscriptions de service.Place extension methods in the Microsoft.Extensions.DependencyInjection namespace to encapsulate groups of service registrations.

Si le constructeur du service exige une primitive, comme string, celle-ci peut être injectée à l’aide de la configuration ou du modèle d’options :If the service's constructor requires a primitive, such as a string, the primitive can be injected by using configuration or the options pattern:

public class MyDependency : IMyDependency
{
    public MyDependency(IConfiguration config)
    {
        var myStringValue = config["MyStringKey"];

        // Use myStringValue
    }

    ...
}

Une instance du service est demandée via le constructeur d’une classe dans laquelle le service est utilisé et assigné à un champ privé.An instance of the service is requested via the constructor of a class where the service is used and assigned to a private field. Le champ est utilisé pour accéder au service en fonction des besoins tout au long de la classe.The field is used to access the service as necessary throughout the class.

Dans l’exemple d’application, l’instance IMyDependency est demandée et utilisée pour appeler la méthode WriteMessage du service :In the sample app, the IMyDependency instance is requested and used to call the service's WriteMessage method:

public class IndexModel : PageModel
{
    private readonly IMyDependency _myDependency;

    public IndexModel(
        IMyDependency myDependency, 
        OperationService operationService,
        IOperationTransient transientOperation,
        IOperationScoped scopedOperation,
        IOperationSingleton singletonOperation,
        IOperationSingletonInstance singletonInstanceOperation)
    {
        _myDependency = myDependency;
        OperationService = operationService;
        TransientOperation = transientOperation;
        ScopedOperation = scopedOperation;
        SingletonOperation = singletonOperation;
        SingletonInstanceOperation = singletonInstanceOperation;
    }

    public OperationService OperationService { get; }
    public IOperationTransient TransientOperation { get; }
    public IOperationScoped ScopedOperation { get; }
    public IOperationSingleton SingletonOperation { get; }
    public IOperationSingletonInstance SingletonInstanceOperation { get; }

    public async Task OnGetAsync()
    {
        await _myDependency.WriteMessage(
            "IndexModel.OnGetAsync created this message.");
    }
}
public class MyDependencyController : Controller
{
    private readonly IMyDependency _myDependency;

    public MyDependencyController(IMyDependency myDependency)
    {
        _myDependency = myDependency;
    }

    // GET: /mydependency/
    public async Task<IActionResult> Index()
    {
        await _myDependency.WriteMessage(
            "MyDependencyController.Index created this message.");

        return View();
    }
}

Services fournis par le frameworkFramework-provided services

La méthode Startup.ConfigureServices est chargée de définir les services utilisés par l’application, notamment les fonctionnalités de plateforme comme Entity Framework Core et ASP.NET Core MVC.The Startup.ConfigureServices method is responsible for defining the services the app uses, including platform features, such as Entity Framework Core and ASP.NET Core MVC. Au départ, la valeur IServiceCollection fournie à ConfigureServices a les services suivants définis (en fonction de la manière dont l’hôte a été configuré) :Initially, the IServiceCollection provided to ConfigureServices has the following services defined (depending on how the host was configured):

Type de serviceService Type Durée de vieLifetime
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactoryMicrosoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory TransientTransient
Microsoft.AspNetCore.Hosting.IApplicationLifetimeMicrosoft.AspNetCore.Hosting.IApplicationLifetime SingletonSingleton
Microsoft.AspNetCore.Hosting.IHostingEnvironmentMicrosoft.AspNetCore.Hosting.IHostingEnvironment SingletonSingleton
Microsoft.AspNetCore.Hosting.IStartupMicrosoft.AspNetCore.Hosting.IStartup SingletonSingleton
Microsoft.AspNetCore.Hosting.IStartupFilterMicrosoft.AspNetCore.Hosting.IStartupFilter TransientTransient
Microsoft.AspNetCore.Hosting.Server.IServerMicrosoft.AspNetCore.Hosting.Server.IServer SingletonSingleton
Microsoft.AspNetCore.Http.IHttpContextFactoryMicrosoft.AspNetCore.Http.IHttpContextFactory TransientTransient
Microsoft.Extensions.Logging.ILogger<T>Microsoft.Extensions.Logging.ILogger<T> SingletonSingleton
Microsoft.Extensions.Logging.ILoggerFactoryMicrosoft.Extensions.Logging.ILoggerFactory SingletonSingleton
Microsoft.Extensions.ObjectPool.ObjectPoolProviderMicrosoft.Extensions.ObjectPool.ObjectPoolProvider SingletonSingleton
Microsoft.Extensions.Options.IConfigureOptions<T>Microsoft.Extensions.Options.IConfigureOptions<T> TransientTransient
Microsoft.Extensions.Options.IOptions<T>Microsoft.Extensions.Options.IOptions<T> SingletonSingleton
System.Diagnostics.DiagnosticSourceSystem.Diagnostics.DiagnosticSource SingletonSingleton
System.Diagnostics.DiagnosticListenerSystem.Diagnostics.DiagnosticListener SingletonSingleton

Lorsqu’une méthode d’extension de collection de services est disponible pour inscrire un service (et ses services dépendants, si nécessaire), la convention consiste à utiliser une seule méthode d’extension Add{SERVICE_NAME} pour inscrire tous les services requis par ce service.When a service collection extension method is available to register a service (and its dependent services, if required), the convention is to use a single Add{SERVICE_NAME} extension method to register all of the services required by that service. Le code suivant est un exemple montrant comment ajouter des services supplémentaires au conteneur en utilisant les méthodes d’extension AddDbContext, AddIdentity et AddMvc :The following code is an example of how to add additional services to the container using the extension methods AddDbContext, AddIdentity, and AddMvc:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    services.AddMvc();
}

Pour plus d’informations, consultez la classe ServiceCollection dans la documentation de l’API.For more information, see the ServiceCollection Class in the API documentation.

Durées de vie du serviceService lifetimes

Choisissez une durée de vie appropriée pour chaque service inscrit.Choose an appropriate lifetime for each registered service. Vous pouvez configurer les services ASP.NET Core avec les durées de vie suivantes :ASP.NET Core services can be configured with the following lifetimes:

TransientTransient

Des services à durée de vie temporaire (Transient) sont créés chaque fois qu’ils sont demandés.Transient lifetime services are created each time they're requested. Cette durée de vie convient parfaitement aux services légers et sans état.This lifetime works best for lightweight, stateless services.

ScopedScoped

Les services à durée de vie délimitée (Scoped) sont créés une seule fois par requête.Scoped lifetime services are created once per request.

Avertissement

Si vous utilisez un service Scoped dans un middleware, injectez le service dans la méthode Invoke ou InvokeAsync.When using a scoped service in a middleware, inject the service into the Invoke or InvokeAsync method. Ne faites pas l’injection via l’injection du constructeur, car elle force le service à se comporter comme un singleton.Don't inject via constructor injection because it forces the service to behave like a singleton. Pour plus d'informations, consultez Intergiciel (middleware) ASP.NET Core.For more information, see Intergiciel (middleware) ASP.NET Core.

SingletonSingleton

Les services avec une durée de vie singleton sont créés la première fois qu’ils sont demandés (ou lorsque ConfigureServices est exécuté et qu’une instance est spécifiée avec l’inscription du service).Singleton lifetime services are created the first time they're requested (or when ConfigureServices is run and an instance is specified with the service registration). Chaque requête ultérieure utilise la même instance.Every subsequent request uses the same instance. Si l’application exige un comportement singleton, il est recommandé d’autoriser le conteneur de service à gérer la durée de vie du service.If the app requires singleton behavior, allowing the service container to manage the service's lifetime is recommended. N’implémentez pas le modèle de conception singleton et fournissez le code utilisateur pour gérer la durée de vie de l’objet dans la classe.Don't implement the singleton design pattern and provide user code to manage the object's lifetime in the class.

Avertissement

Il est dangereux de résoudre un service délimité depuis un singleton.It's dangerous to resolve a scoped service from a singleton. L’état du service risque de ne pas être correct lors du traitement des requêtes suivantes.It may cause the service to have incorrect state when processing subsequent requests.

Comportement d’injection de constructeursConstructor injection behavior

Les services peuvent être résolus par deux mécanismes :Services can be resolved by two mechanisms:

  • IServiceProvider
  • ActivatorUtilities – autorise la création d’objet sans inscription du service dans le conteneur d’injection de dépendances.ActivatorUtilities – Permits object creation without service registration in the dependency injection container. ActivatorUtilities est utilisé avec les abstractions orientées utilisateur, telles que les Tag Helpers, les contrôleurs MVC et les classeurs de modèles.ActivatorUtilities is used with user-facing abstractions, such as Tag Helpers, MVC controllers, and model binders.

Les constructeurs peuvent accepter des arguments qui ne sont pas fournis par l’injection de dépendances, mais les arguments doivent affecter des valeurs par défaut.Constructors can accept arguments that aren't provided by dependency injection, but the arguments must assign default values.

Lorsque des services sont résolus par IServiceProvider ou ActivatorUtilities, l’injection de constructeurs exige un constructeur public.When services are resolved by IServiceProvider or ActivatorUtilities, constructor injection requires a public constructor.

Lorsque des services sont résolus par ActivatorUtilities, l’injection de constructeurs exige qu’un seul constructeur applicable existe.When services are resolved by ActivatorUtilities, constructor injection requires that only one applicable constructor exists. Les surcharges de constructeurs sont prises en charge, mais une seule peut exister dont les arguments peuvent tous être satisfaits par l’injection de dépendances.Constructor overloads are supported, but only one overload can exist whose arguments can all be fulfilled by dependency injection.

Contextes Entity FrameworkEntity Framework contexts

Vous devez ajouter des contextes Entity Framework au conteneur de services en utilisant la durée de vie délimitée.Entity Framework contexts should be added to the service container using the scoped lifetime. Ceci est géré automatiquement par un appel à la méthode AddDbContext lors de l’inscription du contexte de base de données.This is handled automatically with a call to the AddDbContext method when registering the database context. Les services qui utilisent le contexte de base de données doivent également utiliser la durée de vie délimitée.Services that use the database context should also use the scoped lifetime.

Options de durée de vie et d’inscriptionLifetime and registration options

Pour illustrer la différence entre les options de durée de vie et d’inscription, considérez les interfaces suivantes qui représentent des tâches en tant qu’opération avec un identificateur unique, OperationId.To demonstrate the difference between the lifetime and registration options, consider the following interfaces that represent tasks as an operation with a unique identifier, OperationId. Selon la façon dont la durée de vie d’un service d’opérations est configurée pour les interfaces suivantes, le conteneur fournit la même instance ou une instance différente du service lorsqu’une classe le demande :Depending on how the lifetime of an operations service is configured for the following interfaces, the container provides either the same or a different instance of the service when requested by a class:

public interface IOperation
{
    Guid OperationId { get; }
}

public interface IOperationTransient : IOperation
{
}

public interface IOperationScoped : IOperation
{
}

public interface IOperationSingleton : IOperation
{
}

public interface IOperationSingletonInstance : IOperation
{
}
public interface IOperation
{
    Guid OperationId { get; }
}

public interface IOperationTransient : IOperation
{
}

public interface IOperationScoped : IOperation
{
}

public interface IOperationSingleton : IOperation
{
}

public interface IOperationSingletonInstance : IOperation
{
}

Les interfaces sont implémentées dans la classe Operation.The interfaces are implemented in the Operation class. Le constructeur Operation génère un GUID s’il n’est pas fourni :The Operation constructor generates a GUID if one isn't supplied:

public class Operation : IOperationTransient, 
    IOperationScoped, 
    IOperationSingleton, 
    IOperationSingletonInstance
{
    public Operation() : this(Guid.NewGuid())
    {
    }

    public Operation(Guid id)
    {
        OperationId = id;
    }

    public Guid OperationId { get; private set; }
}
public class Operation : IOperationTransient, 
    IOperationScoped, 
    IOperationSingleton, 
    IOperationSingletonInstance
{
    public Operation() : this(Guid.NewGuid())
    {
    }

    public Operation(Guid id)
    {
        OperationId = id;
    }

    public Guid OperationId { get; private set; }
}

Un OperationService est inscrit, dépendant de chacun des autres types Operation.An OperationService is registered that depends on each of the other Operation types. Lorsque OperationService est demandé via l’injection de dépendances, il reçoit une nouvelle instance de chaque service ou une instance existante en fonction de la durée de vie du service dépendant.When OperationService is requested via dependency injection, it receives either a new instance of each service or an existing instance based on the lifetime of the dependent service.

  • Si des services temporaires sont créés à la demande, le OperationId du service IOperationTransient est différent du OperationId de OperationService.If transient services are created when requested, the OperationId of the IOperationTransient service is different than the OperationId of the OperationService. OperationService reçoit une nouvelle instance de la classe IOperationTransient.OperationService receives a new instance of the IOperationTransient class. La nouvelle instance génère un autre OperationId.The new instance yields a different OperationId.
  • Si des services délimités sont créés pour chaque requête, le OperationId du IOperationScoped service est identique à celui de OperationService au sein d’une requête.If scoped services are created per request, the OperationId of the IOperationScoped service is the same as that of OperationService within a request. Entre les requêtes, les deux services partagent une valeur OperationId différente.Across requests, both services share a different OperationId value.
  • Si des services singleton et d’instances singleton sont créés une fois et utilisés sur toutes les requêtes et tous les services, le OperationId est constant entre toutes les requêtes de service.If singleton and singleton-instance services are created once and used across all requests and all services, the OperationId is constant across all service requests.
public class OperationService
{
    public OperationService(
        IOperationTransient transientOperation,
        IOperationScoped scopedOperation,
        IOperationSingleton singletonOperation,
        IOperationSingletonInstance instanceOperation)
    {
        TransientOperation = transientOperation;
        ScopedOperation = scopedOperation;
        SingletonOperation = singletonOperation;
        SingletonInstanceOperation = instanceOperation;
    }

    public IOperationTransient TransientOperation { get; }
    public IOperationScoped ScopedOperation { get; }
    public IOperationSingleton SingletonOperation { get; }
    public IOperationSingletonInstance SingletonInstanceOperation { get; }
}
public class OperationService
{
    public OperationService(IOperationTransient transientOperation,
        IOperationScoped scopedOperation,
        IOperationSingleton singletonOperation,
        IOperationSingletonInstance instanceOperation)
    {
        TransientOperation = transientOperation;
        ScopedOperation = scopedOperation;
        SingletonOperation = singletonOperation;
        SingletonInstanceOperation = instanceOperation;
    }

    public IOperationTransient TransientOperation { get; }
    public IOperationScoped ScopedOperation { get; }
    public IOperationSingleton SingletonOperation { get; }
    public IOperationSingletonInstance SingletonInstanceOperation { get; }
}

Dans Startup.ConfigureServices, chaque type est ajouté au conteneur en fonction de sa durée de vie nommée :In Startup.ConfigureServices, each type is added to the container according to its named lifetime:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    services.AddScoped<IMyDependency, MyDependency>();
    services.AddTransient<IOperationTransient, Operation>();
    services.AddScoped<IOperationScoped, Operation>();
    services.AddSingleton<IOperationSingleton, Operation>();
    services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));

    // OperationService depends on each of the other Operation types.
    services.AddTransient<OperationService, OperationService>();
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddScoped<IMyDependency, MyDependency>();
    services.AddTransient<IOperationTransient, Operation>();
    services.AddScoped<IOperationScoped, Operation>();
    services.AddSingleton<IOperationSingleton, Operation>();
    services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));

    // OperationService depends on each of the other Operation types.
    services.AddTransient<OperationService, OperationService>();
}

Le service IOperationSingletonInstance utilise une instance spécifique avec un ID connu Guid.Empty.The IOperationSingletonInstance service is using a specific instance with a known ID of Guid.Empty. L’utilisation de ce type est facilement identifiable (son GUID n’affiche que des zéros).It's clear when this type is in use (its GUID is all zeroes).

L’exemple d’application montre les durées de vie des objets au sein et entre des requêtes individuelles.The sample app demonstrates object lifetimes within and between individual requests. L’exemple d’application IndexModel demande chaque type IOperation et OperationService.The sample app's IndexModel requests each kind of IOperation type and the OperationService. La page affiche ensuite l’ensemble de la classe du modèle de page et des valeurs OperationId du service via des assignations de propriété :The page then displays all of the page model class's and service's OperationId values through property assignments:

public class IndexModel : PageModel
{
    private readonly IMyDependency _myDependency;

    public IndexModel(
        IMyDependency myDependency, 
        OperationService operationService,
        IOperationTransient transientOperation,
        IOperationScoped scopedOperation,
        IOperationSingleton singletonOperation,
        IOperationSingletonInstance singletonInstanceOperation)
    {
        _myDependency = myDependency;
        OperationService = operationService;
        TransientOperation = transientOperation;
        ScopedOperation = scopedOperation;
        SingletonOperation = singletonOperation;
        SingletonInstanceOperation = singletonInstanceOperation;
    }

    public OperationService OperationService { get; }
    public IOperationTransient TransientOperation { get; }
    public IOperationScoped ScopedOperation { get; }
    public IOperationSingleton SingletonOperation { get; }
    public IOperationSingletonInstance SingletonInstanceOperation { get; }

    public async Task OnGetAsync()
    {
        await _myDependency.WriteMessage(
            "IndexModel.OnGetAsync created this message.");
    }
}

L’exemple d’application montre les durées de vie des objets au sein et entre des requêtes individuelles.The sample app demonstrates object lifetimes within and between individual requests. L’exemple d’application inclut un OperationsController qui demande chaque type IOperation et OperationService.The sample app includes an OperationsController that requests each kind of IOperation type and the OperationService. L’action Index définit les services dans ViewBag pour l’affichage des valeurs OperationId du service :The Index action sets the services into the ViewBag for display of the service's OperationId values:

public class OperationsController : Controller
{
    private readonly OperationService _operationService;
    private readonly IOperationTransient _transientOperation;
    private readonly IOperationScoped _scopedOperation;
    private readonly IOperationSingleton _singletonOperation;
    private readonly IOperationSingletonInstance _singletonInstanceOperation;

    public OperationsController(
        OperationService operationService,
        IOperationTransient transientOperation,
        IOperationScoped scopedOperation,
        IOperationSingleton singletonOperation,
        IOperationSingletonInstance singletonInstanceOperation)
    {
        _operationService = operationService;
        _transientOperation = transientOperation;
        _scopedOperation = scopedOperation;
        _singletonOperation = singletonOperation;
        _singletonInstanceOperation = singletonInstanceOperation;
    }

    public IActionResult Index()
    {
        // Viewbag contains controller-requested services.
        ViewBag.Transient = _transientOperation;
        ViewBag.Scoped = _scopedOperation;
        ViewBag.Singleton = _singletonOperation;
        ViewBag.SingletonInstance = _singletonInstanceOperation;
        
        // Operation service has its own requested services.
        ViewBag.Service = _operationService;

        return View();
    }
}

Les deux sorties suivantes montrent les résultats de deux requêtes :Two following output shows the results of two requests:

Première requête :First request:

Opérations du contrôleur :Controller operations:

Transient: d233e165-f417-469b-a866-1cf1935d2518Transient: d233e165-f417-469b-a866-1cf1935d2518
Scoped: 5d997e2d-55f5-4a64-8388-51c4e3a1ad19Scoped: 5d997e2d-55f5-4a64-8388-51c4e3a1ad19
Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
Instance: 00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

Opérations OperationService :OperationService operations:

Transient: c6b049eb-1318-4e31-90f1-eb2dd849ff64Transient: c6b049eb-1318-4e31-90f1-eb2dd849ff64
Scoped: 5d997e2d-55f5-4a64-8388-51c4e3a1ad19Scoped: 5d997e2d-55f5-4a64-8388-51c4e3a1ad19
Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
Instance: 00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

Deuxième requête :Second request:

Opérations du contrôleur :Controller operations:

Transient: b63bd538-0a37-4ff1-90ba-081c5138dda0Transient: b63bd538-0a37-4ff1-90ba-081c5138dda0
Scoped: 31e820c5-4834-4d22-83fc-a60118acb9f4Scoped: 31e820c5-4834-4d22-83fc-a60118acb9f4
Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
Instance: 00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

Opérations OperationService :OperationService operations:

Transient: c4cbacb8-36a2-436d-81c8-8c1b78808aafTransient: c4cbacb8-36a2-436d-81c8-8c1b78808aaf
Scoped: 31e820c5-4834-4d22-83fc-a60118acb9f4Scoped: 31e820c5-4834-4d22-83fc-a60118acb9f4
Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9Singleton: 01271bc1-9e31-48e7-8f7c-7261b040ded9
Instance: 00000000-0000-0000-0000-000000000000Instance: 00000000-0000-0000-0000-000000000000

Observez les valeurs OperationId qui varient au sein d’une requête et entre les requêtes :Observe which of the OperationId values vary within a request and between requests:

  • Les objets Transient sont toujours différents.Transient objects are always different. Notez que les valeurs OperationId transitoires pour la première et la deuxième demande sont différentes pour les deux opérations OperationService et entre les requêtes.Note that the transient OperationId value for both the first and second requests are different for both OperationService operations and across requests. Une nouvelle instance est fournie à chaque service et requête.A new instance is provided to each service and request.
  • Les objets Scoped sont les mêmes au sein d’une requête, mais ils diffèrent entre les requêtes.Scoped objects are the same within a request but different across requests.
  • Les objets Singleton sont les mêmes pour chaque objet et chaque requête, qu’une instance Operation soit fournie dans ConfigureServices ou non.Singleton objects are the same for every object and every request regardless of whether an Operation instance is provided in ConfigureServices.

Appeler des services à partir de MainCall services from main

Créez un IServiceScope avec IServiceScopeFactory.CreateScope pour résoudre un service Scoped dans le scope de l’application.Create an IServiceScope with IServiceScopeFactory.CreateScope to resolve a scoped service within the app's scope. Cette approche est pratique pour accéder à un service Scoped au démarrage pour exécuter des tâches d’initialisation.This approach is useful to access a scoped service at startup to run initialization tasks. L’exemple suivant montre comment obtenir un contexte pour MyScopedService dans Program.Main :The following example shows how to obtain a context for the MyScopedService in Program.Main:

public static void Main(string[] args)
{
    var host = CreateWebHostBuilder(args).Build();

    using (var serviceScope = host.Services.CreateScope())
    {
        var services = serviceScope.ServiceProvider;

        try
        {
            var serviceContext = services.GetRequiredService<MyScopedService>();
            // Use the context here
        }
        catch (Exception ex)
        {
            var logger = services.GetRequiredService<ILogger<Program>>();
            logger.LogError(ex, "An error occurred.");
        }
    }

    host.Run();
}

Validation de l’étendueScope validation

Quand l’application s’exécute dans l’environnement de développement, le fournisseur de services par défaut effectue des contrôles pour vérifier que :When the app is running in the Development environment, the default service provider performs checks to verify that:

  • Les services Scoped ne sont pas résolus directement ou indirectement à partir du fournisseur de services racine.Scoped services aren't directly or indirectly resolved from the root service provider.
  • Les services Scoped ne sont pas directement ou indirectement injectés dans des singletons.Scoped services aren't directly or indirectly injected into singletons.

Le fournisseur de services racine est créé quand BuildServiceProvider est appelé.The root service provider is created when BuildServiceProvider is called. La durée de vie du fournisseur de services racine correspond à la durée de vie de l’application/du serveur quand le fournisseur démarre avec l’application et qu’il est supprimé quand l’application s’arrête.The root service provider's lifetime corresponds to the app/server's lifetime when the provider starts with the app and is disposed when the app shuts down.

Les services Scoped sont supprimés par le conteneur qui les a créés.Scoped services are disposed by the container that created them. Si un service Scoped est créé dans le conteneur racine, la durée de vie du service est promue en singleton, car elle est supprimée par le conteneur racine seulement quand l’application/le serveur est arrêté.If a scoped service is created in the root container, the service's lifetime is effectively promoted to singleton because it's only disposed by the root container when app/server is shut down. La validation des étendues du service permet de traiter ces situations quand BuildServiceProvider est appelé.Validating service scopes catches these situations when BuildServiceProvider is called.

Pour plus d'informations, consultez Hôte web ASP.NET Core.For more information, see Hôte web ASP.NET Core.

Services de requêteRequest Services

Les services disponibles au sein d’une requête ASP.NET Core à partir de HttpContext sont exposés par le biais de la collection HttpContext.RequestServices.The services available within an ASP.NET Core request from HttpContext are exposed through the HttpContext.RequestServices collection.

Les services de requête représentent les services configurés et demandés dans le cadre de l’application.Request Services represent the services configured and requested as part of the app. Lorsque les objets spécifient des dépendances, ceux-ci sont satisfaits par les types trouvés dans RequestServices, pas dans ApplicationServices.When the objects specify dependencies, these are satisfied by the types found in RequestServices, not ApplicationServices.

En règle générale, l’application ne doit pas utiliser directement ces propriétés.Generally, the app shouldn't use these properties directly. Demandez plutôt les types nécessaires à la classe via des constructeurs de classe et autorisez le framework à injecter les dépendances.Instead, request the types that classes require via class constructors and allow the framework inject the dependencies. Cela génère des classes qui sont plus faciles à tester.This yields classes that are easier to test.

Note

Préférez demander des dépendances en tant que paramètres de constructeur plutôt qu’accéder à la collection RequestServices.Prefer requesting dependencies as constructor parameters to accessing the RequestServices collection.

Conception de services pour l’injection de dépendancesDesign services for dependency injection

Les bonnes pratiques permettent de :Best practices are to:

  • Concevoir des services afin d’utiliser l’injection de dépendances pour obtenir leurs dépendances.Design services to use dependency injection to obtain their dependencies.
  • Éviter les appels de méthode statiques avec état (une pratique appelée static cling).Avoid stateful, static method calls (a practice known as static cling).
  • Éviter une instanciation directe de classes dépendantes au sein de services.Avoid direct instantiation of dependent classes within services. L’instanciation directe associe le code à une implémentation particulière.Direct instantiation couples the code to a particular implementation.

En suivant les principes SOLID de conception orientée objet, les classes d’application ont naturellement tendance à être petites, bien factorisées et facilement testées.By following the SOLID Principles of Object Oriented Design, app classes naturally tend to be small, well-factored, and easily tested.

Si une classe semble avoir trop de dépendances injectées, cela signifie généralement que la classe a trop de responsabilités et viole le principe de responsabilité unique.If a class seems to have too many injected dependencies, this is generally a sign that the class has too many responsibilities and is violating the Single Responsibility Principle (SRP). Essayez de refactoriser la classe en déplaçant certaines de ses responsabilités dans une nouvelle classe.Attempt to refactor the class by moving some of its responsibilities into a new class. N’oubliez pas que les classes du modèle de page Razor Pages et les classes du contrôleur MVC doivent se concentrer sur les problèmes d’interface utilisateur.Keep in mind that Razor Pages page model classes and MVC controller classes should focus on UI concerns. Les règles d’entreprise et les détails d’implémentation de l’accès aux données doivent être conservés dans les classes appropriées à ces préoccupations distinctes.Business rules and data access implementation details should be kept in classes appropriate to these separate concerns.

Suppression des servicesDisposal of services

Le conteneur appelle Dispose pour les types IDisposable qu’il crée.The container calls Dispose for the IDisposable types it creates. Si une instance est ajoutée au conteneur par le code utilisateur, elle n’est pas supprimée automatiquement.If an instance is added to the container by user code, it isn't disposed automatically.

// Services that implement IDisposable:
public class Service1 : IDisposable {}
public class Service2 : IDisposable {}
public class Service3 : IDisposable {}

public interface ISomeService {}
public class SomeServiceImplementation : ISomeService, IDisposable {}

public void ConfigureServices(IServiceCollection services)
{
    // The container creates the following instances and disposes them automatically:
    services.AddScoped<Service1>();
    services.AddSingleton<Service2>();
    services.AddSingleton<ISomeService>(sp => new SomeServiceImplementation());

    // The container doesn't create the following instances, so it doesn't dispose of
    // the instances automatically:
    services.AddSingleton<Service3>(new Service3());
    services.AddSingleton(new Service3());
}

Note

Dans ASP.NET Core 1.0, les appels du conteneur suppriment tous les objets IDisposable, y compris ceux qu’il n’a pas créés.In ASP.NET Core 1.0, the container calls dispose on all IDisposable objects, including those it didn't create.

Remplacement de conteneur de services par défautDefault service container replacement

Le conteneur de services intégré a vocation à répondre aux besoins du framework et de la plupart des applications consommatrices.The built-in service container is meant to serve the needs of the framework and most consumer apps. Nous vous recommandons d’utiliser le conteneur intégré, sauf si vous avez besoin d’une fonctionnalité spécifique qu’il ne prend pas en charge.We recommend using the built-in container unless you need a specific feature that it doesn't support. Voici quelques-unes des fonctionnalités prises en charge dans des conteneurs tiers qui sont introuvables dans le conteneur intégré :Some of the features supported in 3rd party containers not found in the built-in container:

  • Injection de propriétésProperty injection
  • Injection en fonction du nomInjection based on name
  • Conteneurs enfantsChild containers
  • Gestion personnalisée de la durée de vieCustom lifetime management
  • Func<T> prend en charge l’initialisation tardiveFunc<T> support for lazy initialization

Pour obtenir une liste de certains des conteneurs qui prennent en charge des adaptateurs, consultez le fichier readme.md relatif à l’injection de dépendances.See the Dependency Injection readme.md file for a list of some of the containers that support adapters.

L’exemple suivant remplace le conteneur intégré par Autofac :The following sample replaces the built-in container with Autofac:

  • Installez les packages de conteneurs appropriés :Install the appropriate container package(s):

  • Configurez le conteneur dans Startup.ConfigureServices et retournez un IServiceProvider :Configure the container in Startup.ConfigureServices and return an IServiceProvider:

    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
        // Add other framework services
    
        // Add Autofac
        var containerBuilder = new ContainerBuilder();
        containerBuilder.RegisterModule<DefaultModule>();
        containerBuilder.Populate(services);
        var container = containerBuilder.Build();
        return new AutofacServiceProvider(container);
    }
    

    Pour utiliser un conteneur tiers, Startup.ConfigureServices doit retourner IServiceProvider.To use a 3rd party container, Startup.ConfigureServices must return IServiceProvider.

  • Configurez Autofac dans DefaultModule :Configure Autofac in DefaultModule:

    public class DefaultModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<CharacterRepository>().As<ICharacterRepository>();
        }
    }
    

Au moment de l’exécution, Autofac est utilisé pour résoudre les types et injecter des dépendances.At runtime, Autofac is used to resolve types and inject dependencies. Pour en savoir plus sur l’utilisation d’Autofac avec ASP.NET Core, consultez la documentation Autofac.To learn more about using Autofac with ASP.NET Core, see the Autofac documentation.

Sécurité des threadsThread safety

Les services singleton doivent être thread-safe.Singleton services need to be thread safe. Si un service singleton a une dépendance vis-à-vis d’un service Transient, ce dernier peut également avoir besoin d’être thread-safe selon la manière dont il est utilisé par le singleton.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.

La méthode de fabrique d’un service individuel, comme le deuxième argument de AddSingleton<TService>(IServiceCollection, Func<IServiceProvider, TService>), ne doit être thread-safe.The factory method of single service, such as the second argument to AddSingleton<TService>(IServiceCollection, Func<IServiceProvider,TService>), doesn't need to be thread-safe. Comme un constructeur de type (static), elle est forcément appelée une fois par un seul thread.Like a type (static) constructor, it's guaranteed to be called once by a single thread.

RecommandationsRecommendations

  • La résolution de service basée sur async/await et Task n’est pas prise en charge.async/await and Task based service resolution is not supported. C# ne prend pas en charge les constructeurs asynchrones. Par conséquent, le modèle recommandé consiste à utiliser des méthodes asynchrones après avoir résolu de façon synchrone le service.C# does not support asynchronous constructors, therefore the recommended pattern is to use asynchronous methods after synchronously resolving the service.

  • Évitez de stocker des données et des configurations directement dans le conteneur de services.Avoid storing data and configuration directly in the service container. Par exemple, le panier d’achat d’un utilisateur ne doit en général pas être ajouté au conteneur de services.For example, a user's shopping cart shouldn't typically be added to the service container. La configuration doit utiliser le modèle d’options.Configuration should use the options pattern. De même, évitez les objets « conteneurs de données » qui n’existent que pour autoriser l’accès à un autre objet.Similarly, avoid "data holder" objects that only exist to allow access to some other object. Il est préférable de demander l’élément réel par le biais de l’injection de dépendance.It's better to request the actual item via DI.

  • Évitez l’accès statique aux services (par exemple avec IApplicationBuilder.ApplicationServices à utiliser ailleurs).Avoid static access to services (for example, statically-typing IApplicationBuilder.ApplicationServices for use elsewhere).

  • Évitez d’utiliser le modèle de localisation de service.Avoid using the service locator pattern. Par exemple, n’appelez pas GetService pour obtenir une instance de service lorsque vous pouvez utiliser l’injection de dépendance à la place.For example, don't invoke GetService to obtain a service instance when you can use DI instead. Une autre variante du localisateur de service à éviter est l’injection d’une fabrique qui résout les dépendances au moment de l’exécution.Another service locator variation to avoid is injecting a factory that resolves dependencies at runtime. Ces deux pratiques combinent des stratégies Inversion de contrôle.Both of these practices mix Inversion of Control strategies.

  • Évitez l’accès statique à HttpContext (par exemple, IHttpContextAccessor.HttpContext).Avoid static access to HttpContext (for example, IHttpContextAccessor.HttpContext).

Comme pour toutes les recommandations, vous pouvez vous trouver dans des situations où il est nécessaire d’ignorer une recommandation.Like all sets of recommendations, you may encounter situations where ignoring a recommendation is required. Les exceptions sont rares et représentent principalement des cas spéciaux dans le framework lui-même.Exceptions are rare—mostly special cases within the framework itself.

L’injection de dépendance constitue une alternative aux modèles d’accès aux objets statiques/globaux.DI is an alternative to static/global object access patterns. Il est possible que vous ne bénéficiez pas des avantages de l’injection de dépendances si vous la combinez avec l’accès aux objets statiques.You may not be able to realize the benefits of DI if you mix it with static object access.

Ressources supplémentairesAdditional resources