Inserimento delle dipendenze in ASP.NET CoreDependency injection in ASP.NET Core

Di Steve Smith, Scott Addie e Luke LathamBy Steve Smith, Scott Addie, and Luke Latham

ASP.NET Core supporta il modello progettuale software per l'inserimento delle dipendenze, ovvero una tecnica per ottenere l'inversione del controllo (IoC) tra classi e relative dipendenze.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.

Per altre informazioni specifiche per l'inserimento delle dipendenze all'interno di controller MVC, vedere Inserimento di dipendenze in controller in ASP.NET Core.For more information specific to dependency injection within MVC controllers, see Inserimento di dipendenze in controller in ASP.NET Core.

Visualizzare o scaricare il codice di esempio (procedura per il download)View or download sample code (how to download)

Panoramica dell'inserimento delle dipendenzeOverview of dependency injection

Una dipendenza è qualsiasi oggetto richiesto da un altro oggetto.A dependency is any object that another object requires. Esaminare la classe MyDependency seguente con un metodo WriteMessage da cui dipendono altre classi in un'app: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);
    }
}

È possibile creare un'istanza della classe MyDependency per rendere il metodo WriteMessagedisponibile per una classe.An instance of the MyDependency class can be created to make the WriteMessage method available to a class. La classe MyDependency è una dipendenza della 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.");
    }
}

La classe crea e dipende direttamente dall'istanza MyDependency.The class creates and directly depends on the MyDependency instance. Le dipendenze del codice (come nell'esempio precedente) sono problematiche e devono essere evitate per i motivi seguenti:Code dependencies (such as the previous example) are problematic and should be avoided for the following reasons:

  • Per sostituire MyDependency con un'implementazione diversa, la classe deve essere modificata.To replace MyDependency with a different implementation, the class must be modified.
  • Se MyDependency ha dipendenze, devono essere configurate dalla classe.If MyDependency has dependencies, they must be configured by the class. In un progetto di grandi dimensioni con più classi che dipendono da MyDependency, il codice di configurazione diventa sparso in tutta l'app.In a large project with multiple classes depending on MyDependency, the configuration code becomes scattered across the app.
  • È difficile eseguire unit test di questa implementazione.This implementation is difficult to unit test. L'app dovrebbe usare una classe MyDependency fittizia o stub, ma ciò non è possibile con questo approccio.The app should use a mock or stub MyDependency class, which isn't possible with this approach.

L'inserimento delle dipendenze consente di risolvere questi problemi tramite:Dependency injection addresses these problems through:

  • L'uso di un'interfaccia o di una classe di base per astrarre l'implementazione delle dipendenze.The use of an interface or base class to abstract the dependency implementation.
  • La registrazione della dipendenza in un contenitore di servizi.Registration of the dependency in a service container. ASP.NET Core offre il contenitore di servizi predefinito IServiceProvider.ASP.NET Core provides a built-in service container, IServiceProvider. I servizi vengono registrati nel metodo Startup.ConfigureServices dell'app.Services are registered in the app's Startup.ConfigureServices method.
  • L'inserimento del servizio nel costruttore della classe in cui viene usato.Injection of the service into the constructor of the class where it's used. Il framework si assume la responsabilità della creazione di un'istanza della dipendenza e della sua eliminazione quando non è più necessaria.The framework takes on the responsibility of creating an instance of the dependency and disposing of it when it's no longer needed.

Nell'app di esempio, l'interfaccia IMyDependency definisce un metodo che il servizio fornisce all'app: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);
}

Questa interfaccia viene implementata da un tipo concreto, 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 richiede ILogger<TCategoryName> nel costruttore.MyDependency requests an ILogger<TCategoryName> in its constructor. Non è insolito usare l'inserimento delle dipendenze in modo concatenato.It's not unusual to use dependency injection in a chained fashion. Ogni dipendenza richiesta richiede a sua volta le proprie dipendenze.Each requested dependency in turn requests its own dependencies. Il contenitore risolve le dipendenze nel grafico e restituisce il servizio completamente risolto.The container resolves the dependencies in the graph and returns the fully resolved service. Il set di dipendenze che devono essere risolte viene generalmente chiamato albero delle dipendenze o grafico dipendenze o grafico degli oggetti.The collective set of dependencies that must be resolved is typically referred to as a dependency tree, dependency graph, or object graph.

IMyDependency e ILogger<TCategoryName> devono essere registrati nel contenitore di servizi.IMyDependency and ILogger<TCategoryName> must be registered in the service container. IMyDependency viene registrato in Startup.ConfigureServices.IMyDependency is registered in Startup.ConfigureServices. ILogger<TCategoryName> viene registrato dall'infrastruttura di astrazioni di registrazione, pertanto si tratta di un servizio fornito dal framework registrato per impostazione predefinita dal framework.ILogger<TCategoryName> is registered by the logging abstractions infrastructure, so it's a framework-provided service registered by default by the framework.

Il contenitore risolve ILogger<TCategoryName> avvalendosi di tipi aperti (generici), eliminando l'esigenza di registrare ogni singolo tipo costruito (generico):The container resolves ILogger<TCategoryName> by taking advantage of (generic) open types, eliminating the need to register every (generic) constructed type:

services.AddSingleton(typeof(ILogger<T>), typeof(Logger<T>));

Nell'app di esempio, il servizio IMyDependency viene registrato con il tipo concreto MyDependency.In the sample app, the IMyDependency service is registered with the concrete type MyDependency. La registrazione definisce come ambito della durata di servizio la durata di una singola richiesta.The registration scopes the service lifetime to the lifetime of a single request. Le durate dei servizi sono descritte più avanti in questo argomento.Service lifetimes are described later in this topic.

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();

    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().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

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

Nota

Ogni metodo di estensione services.Add{SERVICE_NAME} aggiunge (e potenzialmente configura) i servizi.Each services.Add{SERVICE_NAME} extension method adds (and potentially configures) services. Ad esempio, services.AddMvc() aggiunge i servizi richiesti da Razor Pages e MVC.For example, services.AddMvc() adds the services Razor Pages and MVC require. È consigliabile che le app seguano questa convenzione.We recommended that apps follow this convention. Inserire i metodi di estensione nello spazio dei nomi Microsoft.Extensions.DependencyInjection per incapsulare i gruppi di registrazioni di servizio.Place extension methods in the Microsoft.Extensions.DependencyInjection namespace to encapsulate groups of service registrations.

Se il costruttore del servizio richiede un tipo incorporato, ad esempio string, è possibile inserirlo usando la configurazione o il modello di opzioni:If the service's constructor requires a built in type, such as a string, the type can be injected by using configuration or the options pattern:

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

        // Use myStringValue
    }

    ...
}

È richiesta un'istanza del servizio tramite il costruttore di una classe in cui il servizio viene usato e assegnato a un campo privato.An instance of the service is requested via the constructor of a class where the service is used and assigned to a private field. Il campo viene usato per accedere al servizio in base alle esigenze per l'intera classe.The field is used to access the service as necessary throughout the class.

Nell'app di esempio, viene richiesta l'istanza IMyDependency usata per chiamare il metodo WriteMessage del servizio: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 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.");
    }
}

Servizi inseriti all'avvioServices injected into Startup

Quando si usa l'host generico (IHostBuilder), è possibile inserire solo i tipi di servizio seguenti nel costruttore Startup:Only the following service types can be injected into the Startup constructor when using the Generic Host (IHostBuilder):

I servizi possono essere inseriti in Startup.Configure:Services can be injected into Startup.Configure:

public void Configure(IApplicationBuilder app, IOptions<MyOptions> options)
{
    ...
}

Per ulteriori informazioni, vedere Avvio dell'app in ASP.NET Core.For more information, see Avvio dell'app in ASP.NET Core.

Servizi forniti dal frameworkFramework-provided services

Il metodo Startup.ConfigureServices è responsabile della definizione dei servizi utilizzati dall'app, incluse le funzionalità della piattaforma, ad esempio Entity Framework Core e ASP.NET Core MVC.The Startup.ConfigureServices method is responsible for defining the services that the app uses, including platform features, such as Entity Framework Core and ASP.NET Core MVC. Inizialmente, la IServiceCollection fornita ai ConfigureServices dispone di servizi definiti dal Framework a seconda della configurazione dell'host.Initially, the IServiceCollection provided to ConfigureServices has services defined by the framework depending on how the host was configured. Non è insolito che un'app basata su un modello di ASP.NET Core disponga di centinaia di servizi registrati dal Framework.It's not uncommon for an app based on an ASP.NET Core template to have hundreds of services registered by the framework. Nella tabella seguente è elencato un piccolo esempio di servizi registrati dal Framework.A small sample of framework-registered services is listed in the following table.

Tipo di servizioService Type DurataLifetime
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory TemporaneoTransient
IHostApplicationLifetime SingletonSingleton
IWebHostEnvironment SingletonSingleton
Microsoft.AspNetCore.Hosting.IStartup SingletonSingleton
Microsoft.AspNetCore.Hosting.IStartupFilter TemporaneoTransient
Microsoft.AspNetCore.Hosting.Server.IServer SingletonSingleton
Microsoft.AspNetCore.Http.IHttpContextFactory TemporaneoTransient
Microsoft.Extensions.Logging.ILogger<TCategoryName> SingletonSingleton
Microsoft.Extensions.Logging.ILoggerFactory SingletonSingleton
Microsoft.Extensions.ObjectPool.ObjectPoolProvider SingletonSingleton
Microsoft.Extensions.Options.IConfigureOptions<TOptions> TemporaneoTransient
Microsoft.Extensions.Options.IOptions<TOptions> SingletonSingleton
System.Diagnostics.DiagnosticSource SingletonSingleton
System.Diagnostics.DiagnosticListener SingletonSingleton
Tipo di servizioService Type DurataLifetime
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory TemporaneoTransient
Microsoft.AspNetCore.Hosting.IApplicationLifetime SingletonSingleton
Microsoft.AspNetCore.Hosting.IHostingEnvironment SingletonSingleton
Microsoft.AspNetCore.Hosting.IStartup SingletonSingleton
Microsoft.AspNetCore.Hosting.IStartupFilter TemporaneoTransient
Microsoft.AspNetCore.Hosting.Server.IServer SingletonSingleton
Microsoft.AspNetCore.Http.IHttpContextFactory TemporaneoTransient
Microsoft.Extensions.Logging.ILogger<TCategoryName> SingletonSingleton
Microsoft.Extensions.Logging.ILoggerFactory SingletonSingleton
Microsoft.Extensions.ObjectPool.ObjectPoolProvider SingletonSingleton
Microsoft.Extensions.Options.IConfigureOptions<TOptions> TemporaneoTransient
Microsoft.Extensions.Options.IOptions<TOptions> SingletonSingleton
System.Diagnostics.DiagnosticSource SingletonSingleton
System.Diagnostics.DiagnosticListener SingletonSingleton

Registrare servizi aggiuntivi con i metodi di estensioneRegister additional services with extension methods

Quando è disponibile un metodo di estensione della raccolta di servizi per la registrazione di un servizio (e dei servizi dipendenti, se necessario), la convenzione consiste nell'usare un singolo metodo di estensione Add{SERVICE_NAME} per registrare tutti i servizi richiesti da tale servizio.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. Il codice seguente è un esempio di come aggiungere altri servizi al contenitore usando i metodi di estensione AddDbContext <TContext > e AddIdentityCore:The following code is an example of how to add additional services to the container using the extension methods AddDbContext<TContext> and AddIdentityCore:

public void ConfigureServices(IServiceCollection services)
{
    ...

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

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

    ...
}

Per altre informazioni, vedere la classe ServiceCollection nella documentazione delle API.For more information, see the ServiceCollection class in the API documentation.

Durate dei serviziService lifetimes

Scegliere una durata appropriata per ogni servizio registrato.Choose an appropriate lifetime for each registered service. I servizi ASP.NET Core possono essere configurati con le impostazioni di durata seguenti:ASP.NET Core services can be configured with the following lifetimes:

TemporaneoTransient

I servizi con durata temporanea (AddTransient) vengono creati ogni volta che vengono richiesti dal contenitore dei servizi.Transient lifetime services (AddTransient) are created each time they're requested from the service container. Questa impostazione di durata è consigliata per i servizi semplici senza stati.This lifetime works best for lightweight, stateless services.

Con ambitoScoped

I servizi con durata con ambito (AddScoped) vengono creati una volta per ogni richiesta client (connessione).Scoped lifetime services (AddScoped) are created once per client request (connection).

Avviso

Quando si usa un servizio con ambito in un middleware, inserire il servizio nel metodo Invoke o InvokeAsync.When using a scoped service in a middleware, inject the service into the Invoke or InvokeAsync method. Evitare l'inserimento tramite il costruttore, che impone al servizio il comportamento singleton.Don't inject via constructor injection because it forces the service to behave like a singleton. Per ulteriori informazioni, vedere Scrivere middleware di ASP.NET Core personalizzato.For more information, see Scrivere middleware di ASP.NET Core personalizzato.

SingletonSingleton

I servizi con durata singleton (AddSingleton) vengono creati la prima volta che vengono richiesti (o quando si esegue Startup.ConfigureServices e viene specificata un'istanza con la registrazione del servizio).Singleton lifetime services (AddSingleton) are created the first time they're requested (or when Startup.ConfigureServices is run and an instance is specified with the service registration). Tutte le richieste successive usano la stessa istanza.Every subsequent request uses the same instance. Se l'app richiede un comportamento singleton, è consigliabile consentire al contenitore del servizio di gestire la durata del servizio.If the app requires singleton behavior, allowing the service container to manage the service's lifetime is recommended. Non implementare lo schema progettuale singleton e fornire codice utente per gestire la durata dell'oggetto nella classe.Don't implement the singleton design pattern and provide user code to manage the object's lifetime in the class.

Avviso

Può essere pericoloso risolvere un servizio con ambito da un singleton.It's dangerous to resolve a scoped service from a singleton. Ciò potrebbe causare uno stato non corretto per il servizio durante l'elaborazione delle richieste successive.It may cause the service to have incorrect state when processing subsequent requests.

Metodi di registrazione del servizioService registration methods

I metodi di estensione per la registrazione del servizio offrono overload che risultano utili in scenari specifici.Service registration extension methods offer overloads that are useful in specific scenarios.

MetodoMethod AutomaticoAutomatic
objectobject
eliminazionedisposal
Selezione multiplaMultiple
implementazioniimplementations
Pass argsPass args
Add{LIFETIME}<{SERVICE}, {IMPLEMENTATION}>()
Esempio:Example:
services.AddSingleton<IMyDep, MyDep>();
YesYes YesYes NoNo
Add{LIFETIME}<{SERVICE}>(sp => new {IMPLEMENTATION})
Esempi:Examples:
services.AddSingleton<IMyDep>(sp => new MyDep());
services.AddSingleton<IMyDep>(sp => new MyDep("A string!"));
YesYes YesYes YesYes
Add{LIFETIME}<{IMPLEMENTATION}>()
Esempio:Example:
services.AddSingleton<MyDep>();
YesYes NoNo NoNo
AddSingleton<{SERVICE}>(new {IMPLEMENTATION})
Esempi:Examples:
services.AddSingleton<IMyDep>(new MyDep());
services.AddSingleton<IMyDep>(new MyDep("A string!"));
NoNo YesYes YesYes
AddSingleton(new {IMPLEMENTATION})
Esempi:Examples:
services.AddSingleton(new MyDep());
services.AddSingleton(new MyDep("A string!"));
NoNo NoNo YesYes

Per ulteriori informazioni sull'eliminazione dei tipi, vedere la sezione Eliminazione dei servizi.For more information on type disposal, see the Disposal of services section. Uno scenario comune per più implementazioni è costituito dai tipi di simulazioni per i test.A common scenario for multiple implementations is mocking types for testing.

I metodi TryAdd{LIFETIME} registrano il servizio solo se esiste già un'implementazione registrata.TryAdd{LIFETIME} methods only register the service if there isn't already an implementation registered.

Nell'esempio seguente, la prima riga registra MyDependency per IMyDependency.In the following example, the first line registers MyDependency for IMyDependency. La seconda riga non ha alcun effetto perché IMyDependency dispone già di un'implementazione registrata:The second line has no effect because IMyDependency already has a registered implementation:

services.AddSingleton<IMyDependency, MyDependency>();
// The following line has no effect:
services.TryAddSingleton<IMyDependency, DifferentDependency>();

Per altre informazioni, vedere:For more information, see:

I metodi TryAddEnumerable(ServiceDescriptor) registrano il servizio solo se non esiste già un'implementazione dello stesso tipo.TryAddEnumerable(ServiceDescriptor) methods only register the service if there isn't already an implementation of the same type. Più servizi vengono risolti tramite IEnumerable<{SERVICE}>.Multiple services are resolved via IEnumerable<{SERVICE}>. Durante la registrazione di servizi, lo sviluppatore desidera aggiungere un'istanza solo se non ne è già stata aggiunta una dello stesso tipo.When registering services, the developer only wants to add an instance if one of the same type hasn't already been added. In genere, questo metodo viene utilizzato dagli autori di librerie per evitare di registrare due copie di un'istanza nel contenitore.Generally, this method is used by library authors to avoid registering two copies of an instance in the container.

Nell'esempio seguente, la prima riga registra MyDep per IMyDep1.In the following example, the first line registers MyDep for IMyDep1. La seconda riga registra MyDep per IMyDep2.The second line registers MyDep for IMyDep2. La seconda riga non ha alcun effetto perché IMyDep1 dispone già di un'implementazione registrata di MyDep:The third line has no effect because IMyDep1 already has a registered implementation of MyDep:

public interface IMyDep1 {}
public interface IMyDep2 {}

public class MyDep : IMyDep1, IMyDep2 {}

services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyDep1, MyDep>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyDep2, MyDep>());
// Two registrations of MyDep for IMyDep1 is avoided by the following line:
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyDep1, MyDep>());

Comportamento dell'inserimento del costruttoreConstructor injection behavior

I servizi possono essere risolti con due meccanismi:Services can be resolved by two mechanisms:

  • IServiceProvider
  • ActivatorUtilitiesActivatorUtilities– Consente la creazione di oggetti senza registrazione del servizio nel contenitore di inserimento delle dipendenze.ActivatorUtilities – Permits object creation without service registration in the dependency injection container. ActivatorUtilities viene usato con astrazioni esposte all'utente, ad esempio helper tag, controller MVC e strumenti di associazione di modelli.ActivatorUtilities is used with user-facing abstractions, such as Tag Helpers, MVC controllers, and model binders.

I costruttori possono accettare argomenti non forniti tramite l'inserimento di dipendenze, ma gli argomenti devono assegnare valori predefiniti.Constructors can accept arguments that aren't provided by dependency injection, but the arguments must assign default values.

Quando i servizi vengono risolti da IServiceProvider o ActivatorUtilities, l'inserimento del costruttore richiede un costruttore pubblico.When services are resolved by IServiceProvider or ActivatorUtilities, constructor injection requires a public constructor.

Quando i servizi vengono risolti da ActivatorUtilities, l'inserimento del costruttore richiede l'esistenza di un solo costruttore applicabile.When services are resolved by ActivatorUtilities, constructor injection requires that only one applicable constructor exists. Sebbene siano supportati gli overload dei costruttori, può essere presente un solo overload i cui argomenti possono essere tutti specificati tramite l'inserimento delle dipendenze.Constructor overloads are supported, but only one overload can exist whose arguments can all be fulfilled by dependency injection.

Contesti di Entity FrameworkEntity Framework contexts

I contesti di Entity Framework vengono in genere aggiunti al contenitore di servizi usando la durata con ambito perché le operazioni di database delle app Web hanno di solito come ambito la richiesta client.Entity Framework contexts are usually added to the service container using the scoped lifetime because web app database operations are normally scoped to the client request. La durata predefinita è con ambito se non viene specificata una durata con un overload AddDbContext<TContext> quando si registra il contesto del database.The default lifetime is scoped if a lifetime isn't specified by an AddDbContext<TContext> overload when registering the database context. I servizi con una durata specificata non devono usare un contesto di database con una durata più breve rispetto al servizio.Services of a given lifetime shouldn't use a database context with a shorter lifetime than the service.

Opzioni di durata e di registrazioneLifetime and registration options

Per illustrare la differenza tra le opzioni di durata e registrazione, si considerino le interfacce seguenti che rappresentano le attività come un'operazione con un identificatore univoco, 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. A seconda del modo in cui la durata di un servizio operativo viene configurata per le interfacce seguenti, il contenitore fornisce la stessa istanza o un'istanza diversa del servizio quando richiesto da una classe: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
{
}

Le interfacce vengono implementate nella classe Operation.The interfaces are implemented in the Operation class. Il costruttore Operation genera un GUID se non ne viene fornito uno: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; }
}

Viene registrato un OperationService che dipende da ognuno degli altri tipi Operation.An OperationService is registered that depends on each of the other Operation types. Quando viene richiesto OperationService tramite l'inserimento delle dipendenze, riceve una nuova istanza di ogni servizio o un'istanza esistente in base alla durata del servizio dipendente.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.

  • Se i servizi temporanei vengono creati quando vengono richiesti dal contenitore, OperationId del servizio IOperationTransient è diverso da OperationId di OperationService.When transient services are created when requested from the container, the OperationId of the IOperationTransient service is different than the OperationId of the OperationService. OperationService riceve una nuova istanza della classe IOperationTransient.OperationService receives a new instance of the IOperationTransient class. La nuova istanza restituisce un OperationId diverso.The new instance yields a different OperationId.
  • Se i servizi con ambito vengono creati per ogni richiesta client, OperationId del servizio IOperationScoped corrisponde a OperationService all'interno di una richiesta client.When scoped services are created per client request, the OperationId of the IOperationScoped service is the same as that of OperationService within a client request. In tutte le richieste client entrambi i servizi condividono un valore OperationId diverso.Across client requests, both services share a different OperationId value.
  • Se i servizi singleton e con istanza singleton vengono creati una sola volta e usati per tutte le richieste client e tutti i servizi, OperationId rimane costante in tutte le richieste di servizio.When singleton and singleton-instance services are created once and used across all client 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; }
}

In Startup.ConfigureServices, ogni tipo viene aggiunto al contenitore in base alla durata denominata:In Startup.ConfigureServices, each type is added to the container according to its named lifetime:

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();

    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().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

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

Il servizio IOperationSingletonInstance usa un'istanza specifica con un ID noto di Guid.Empty.The IOperationSingletonInstance service is using a specific instance with a known ID of Guid.Empty. È chiaro quando questo tipo è in uso (il relativo GUID è composto da tutti zero).It's clear when this type is in use (its GUID is all zeroes).

L'app di esempio dimostra le durate degli oggetti all'interno e tra le singole richieste.The sample app demonstrates object lifetimes within and between individual requests. L'IndexModel dell'app di esempio richiede ogni genere di tipo IOperation e OperationService.The sample app's IndexModel requests each kind of IOperation type and the OperationService. La pagina visualizza quindi tutti i valori OperationId della classe del modello di pagina e del servizio tramite assegnazioni di proprietà: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.");
    }
}
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'output seguente mostra i risultati delle due richieste:Two following output shows the results of two requests:

Prima richiesta:First request:

Operazioni del controller: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

Operazioni di 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

Seconda richiesta:Second request:

Operazioni del controller: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

Operazioni di 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

Osservare i valori OperationId che variano all'interno della richiesta e da una richiesta e l'altra:Observe which of the OperationId values vary within a request and between requests:

  • Gli oggetti temporanei sono sempre diversi.Transient objects are always different. Il valore OperationId temporaneo per la prima e la seconda richiesta client è diverso per entrambe le operazioni OperationService e in tutte le richieste client.The transient OperationId value for both the first and second client requests are different for both OperationService operations and across client requests. Viene fornita una nuova istanza per ogni richiesta di servizio e richiesta client.A new instance is provided to each service request and client request.
  • Gli oggetti con ambito sono gli stessi all'interno di una richiesta client, ma variano nelle diverse richieste client.Scoped objects are the same within a client request but different across client requests.
  • Gli oggetti singleton sono gli stessi per ogni oggetto e ogni richiesta, indipendentemente dal fatto che venga fornita un'istanza Operation in Startup.ConfigureServices.Singleton objects are the same for every object and every request regardless of whether an Operation instance is provided in Startup.ConfigureServices.

Chiamare i servizi da mainCall services from main

Creare un IServiceScopeIServiceScope con IServiceScopeFactory.CreateScope per risolvere un servizio con ambito nell'ambito di applicazione dell'app.Create an IServiceScope with IServiceScopeFactory.CreateScope to resolve a scoped service within the app's scope. Questo approccio è utile per l'accesso a un servizio con ambito all'avvio e per l'esecuzione di attività di inizializzazione.This approach is useful to access a scoped service at startup to run initialization tasks. L'esempio seguente indica come ottenere un contesto per MyScopedService in Program.Main:The following example shows how to obtain a context for the MyScopedService in Program.Main:

using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

public class Program
{
    public static async Task Main(string[] args)
    {
        var host = CreateHostBuilder(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.");
            }
        }

        await host.RunAsync();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

public class Program
{
    public static async Task 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.");
            }
        }

        await host.RunAsync();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}

Convalida dell'ambitoScope validation

Quando l'app viene eseguita nell'ambiente di sviluppo, il provider di servizi predefinito esegue controlli per verificare che:When the app is running in the Development environment, the default service provider performs checks to verify that:

  • I servizi con ambito non vengano risolti direttamente o indirettamente dal provider di servizi radice.Scoped services aren't directly or indirectly resolved from the root service provider.
  • I servizi con ambito non vengano inseriti direttamente o indirettamente in singleton.Scoped services aren't directly or indirectly injected into singletons.

Il provider di servizi radice venga creato con la chiamata di BuildServiceProvider.The root service provider is created when BuildServiceProvider is called. La durata del provider di servizi radice corrisponde alla durata dell'app/server quando il provider viene avviato con l'app e viene eliminato alla chiusura dell'app.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.

I servizi con ambito vengono eliminati dal contenitore che li ha creati.Scoped services are disposed by the container that created them. Se un servizio con ambito viene creato nel contenitore radice, la durata del servizio viene di fatto convertita in singleton, perché il servizio viene eliminato solo dal contenitore radice alla chiusura dell'app/server.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 convalida degli ambiti servizio rileva queste situazioni nel contesto della chiamata di BuildServiceProvider.Validating service scopes catches these situations when BuildServiceProvider is called.

Per ulteriori informazioni, vedere Host Web ASP.NET Core.For more information, see Host Web ASP.NET Core.

Servizi di richiestaRequest Services

I servizi disponibili all'interno di una richiesta ASP.NET Core da HttpContext vengono esposti tramite la raccolta HttpContext.RequestServices.The services available within an ASP.NET Core request from HttpContext are exposed through the HttpContext.RequestServices collection.

I servizi di richiesta rappresentano i servizi configurati e richiesti come parte dell'app.Request Services represent the services configured and requested as part of the app. Quando gli oggetti specificano dipendenze, le dipendenze sono soddisfatte dai tipi individuati in RequestServices, non in ApplicationServices.When the objects specify dependencies, these are satisfied by the types found in RequestServices, not ApplicationServices.

In generale, l'app non deve usare queste proprietà direttamente.Generally, the app shouldn't use these properties directly. Richiedere invece i tipi richiesti dalle classi tramite i costruttori di classi e consentire al framework di inserire le dipendenze.Instead, request the types that classes require via class constructors and allow the framework inject the dependencies. Si ottengono classi più facili da testare.This yields classes that are easier to test.

Nota

È consigliabile richiedere le dipendenze come parametri del costruttore per l'accesso alla raccolta RequestServices.Prefer requesting dependencies as constructor parameters to accessing the RequestServices collection.

Progettare i servizi per l'inserimento di dipendenzeDesign services for dependency injection

Le procedure consigliate prevedono di:Best practices are to:

  • Progettare i servizi per usare l'inserimento delle dipendenze per ottenere le relative dipendenze.Design services to use dependency injection to obtain their dependencies.
  • Evitare membri e classi statiche con stato.Avoid stateful, static classes and members. Progettare le app in modo da usare i servizi singleton, evitando così di creare uno stato globale.Design apps to use singleton services instead, which avoid creating global state.
  • Evitare la creazione diretta di istanze delle classi dipendenti all'interno di servizi.Avoid direct instantiation of dependent classes within services. La creazione diretta di istanze associa il codice a una determinata implementazione.Direct instantiation couples the code to a particular implementation.
  • Fare in modo che le classi siano piccole, con factoring corretto e facili da testare.Make app classes small, well-factored, and easily tested.

Se una classe sembra avere troppe dipendenze inserite, ciò significa in genere che la classe ha un numero eccessivo di responsabilità e sta violando il Single Responsibility Principle (SRP) (principio di responsabilità singola).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). Tentare di effettuare il refactoring della classe spostando alcune delle responsabilità in una nuova classe.Attempt to refactor the class by moving some of its responsibilities into a new class. Tenere presente che le classi del modello di pagina Razor Pages e le classi del controller MVC devono essere incentrate sulle problematiche dell'interfaccia utente.Keep in mind that Razor Pages page model classes and MVC controller classes should focus on UI concerns. Di conseguenza, le regole business e i dettagli di implementazione di accesso ai dati devono essere inseriti in classi appropriate per queste problematiche separate.Business rules and data access implementation details should be kept in classes appropriate to these separate concerns.

Eliminazione dei serviziDisposal of services

Il contenitore chiama Dispose per i tipi IDisposable creati.The container calls Dispose for the IDisposable types it creates. Se un'istanza viene aggiunta al contenitore dal codice utente, non viene eliminata automaticamente.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());
}

Sostituzione del contenitore di servizi predefinitoDefault service container replacement

Il contenitore dei servizi incorporato è progettato per soddisfare le esigenze del Framework e della maggior parte delle app consumer.The built-in service container is designed to serve the needs of the framework and most consumer apps. Si consiglia di usare il contenitore predefinito a meno che non sia necessaria una funzionalità specifica che il contenitore incorporato non supporta, ad esempio:We recommend using the built-in container unless you need a specific feature that the built-in container doesn't support, such as:

  • Inserimento di proprietàProperty injection
  • Inserimento in base al nomeInjection based on name
  • Contenitori figlioChild containers
  • Gestione della durata personalizzataCustom lifetime management
  • Supporto di Func<T> per l'inizializzazione differitaFunc<T> support for lazy initialization

Con ASP.NET Core app è possibile usare i contenitori di terze parti seguenti:The following 3rd party containers can be used with ASP.NET Core apps:

Thread safetyThread safety

Creare servizi singleton thread-safe.Create thread-safe singleton services. Se un servizio singleton ha una dipendenza in un servizio temporaneo, è necessario che anche il servizio temporaneo sia thread-safe, a seconda di come viene usato dal singleton.If a singleton service has a dependency on a transient service, the transient service may also require thread safety depending how it's used by the singleton.

Non è necessario che il metodo factory del singolo servizio, ad esempio il secondo argomento per AddSingleton<TService>(IServiceCollection, Func<IServiceProvider,TService), sia 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. Come nel caso di un costruttore di tipo (static), è garantito che venga chiamato una sola volta da un singolo thread.Like a type (static) constructor, it's guaranteed to be called once by a single thread.

SuggerimentiRecommendations

  • La risoluzione basata sui servizi async/await e Task non è supportata.async/await and Task based service resolution is not supported. Dato che C# non supporta i costruttori asincroni, il modello consigliato consiste nell'usare i metodi asincroni dopo avere risolto in modo sincrono il servizio.C# does not support asynchronous constructors; therefore, the recommended pattern is to use asynchronous methods after synchronously resolving the service.

  • Evitare di archiviare i dati e la configurazione direttamente nel contenitore del servizio.Avoid storing data and configuration directly in the service container. Ad esempio, il carrello acquisti di un utente non dovrebbe in genere essere aggiunto al contenitore del servizio.For example, a user's shopping cart shouldn't typically be added to the service container. La configurazione deve usare il modello di opzioni.Configuration should use the options pattern. Analogamente, evitare gli oggetti "contenitori di dati" che hanno la sola funzione di consentire l'accesso ad altri oggetti.Similarly, avoid "data holder" objects that only exist to allow access to some other object. È preferibile richiedere l'elemento effettivo tramite inserimento delle dipendenze.It's better to request the actual item via DI.

  • Evitare l'accesso statico ai servizi (ad esempio, la tipizzazione statica IApplicationBuilder.ApplicationServices da usare in altre posizioni).Avoid static access to services (for example, statically-typing IApplicationBuilder.ApplicationServices for use elsewhere).

  • Evitare di usare il modello di localizzatore del servizio.Avoid using the service locator pattern. Ad esempio, non richiamare GetService per ottenere un'istanza del servizio quando è invece possibile usare l'inserimento delle dipendenze:For example, don't invoke GetService to obtain a service instance when you can use DI instead:

    Non corretto:Incorrect:

    public class MyClass()
    {
        public void MyMethod()
        {
            var optionsMonitor = 
                _services.GetService<IOptionsMonitor<MyOptions>>();
            var option = optionsMonitor.CurrentValue.Option;
    
            ...
        }
    }
    

    Corretto:Correct:

    public class MyClass
    {
        private readonly IOptionsMonitor<MyOptions> _optionsMonitor;
    
        public MyClass(IOptionsMonitor<MyOptions> optionsMonitor)
        {
            _optionsMonitor = optionsMonitor;
        }
    
        public void MyMethod()
        {
            var option = _optionsMonitor.CurrentValue.Option;
    
            ...
        }
    }
    
  • Un'altra variazione del localizzatore del servizio da evitare è inserire una factory che risolve le dipendenze in fase di esecuzione.Another service locator variation to avoid is injecting a factory that resolves dependencies at runtime. Queste procedure combinano le strategie di inversione del controllo.Both of these practices mix Inversion of Control strategies.

  • Evitare l'accesso statico a HttpContext (ad esempio IHttpContextAccessor.HttpContext).Avoid static access to HttpContext (for example, IHttpContextAccessor.HttpContext).

È tuttavia possibile che in alcuni casi queste raccomandazioni debbano essere ignorate.Like all sets of recommendations, you may encounter situations where ignoring a recommendation is required. Le eccezioni sono rare e principalmente si tratta di casi speciali all'interno del framework stesso.Exceptions are rare—mostly special cases within the framework itself.

L'inserimento di dipendenze è un'alternativa ai modelli di accesso agli oggetti statici/globali.DI is an alternative to static/global object access patterns. Se l'inserimento di dipendenze viene usato con l'accesso agli oggetti statico i vantaggi non saranno evidenti.You may not be able to realize the benefits of DI if you mix it with static object access.

Risorse aggiuntiveAdditional resources