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

Di Kirk Larkin, Steve Smith, Scott Addiee Brandon DahlerBy Kirk Larkin, Steve Smith, Scott Addie, and Brandon Dahler

ASP.NET Core supporta lo schema progettuale di software per l'inserimento di dipendenze, una tecnica per ottenere l'IoC (Inversion of Control) tra le classi e le 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.

Per ulteriori informazioni sull'inserimento delle dipendenze delle opzioni, vedere Modello di opzioni in ASP.NET Core .For more information on dependency injection of options, see Modello di opzioni in ASP.NET Core.

In questo argomento vengono fornite informazioni sull'inserimento delle dipendenze in ASP.NET Core.This topic provides information on dependency injection in ASP.NET Core. Per informazioni sull'uso dell'inserimento di dipendenze nelle app console, vedere inserimento delle dipendenze in .NET.For information on using dependency injection in console apps, see Dependency injection in .NET.

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 è un oggetto da cui dipende un altro oggetto.A dependency is an object that another object depends on. Esaminare la MyDependency classe seguente con un WriteMessage metodo da cui dipendono altre classi:Examine the following MyDependency class with a WriteMessage method that other classes depend on:

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

Una classe può creare un'istanza della MyDependency classe per utilizzare il relativo WriteMessage metodo.A class can create an instance of the MyDependency class to make use of its WriteMessage method. Nell'esempio seguente, la MyDependency classe è una dipendenza della IndexModel classe:In the following example, the MyDependency class is a dependency of the IndexModel class:

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

    public void OnGet()
    {
        _dependency.WriteMessage("IndexModel.OnGet created this message.");
    }
}

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

  • Per sostituire MyDependency con un'implementazione diversa, IndexModel è necessario modificare la classe.To replace MyDependency with a different implementation, the IndexModel class must be modified.
  • Se MyDependency dispone di dipendenze, devono anche essere configurate dalla IndexModel classe.If MyDependency has dependencies, they must also be configured by the IndexModel 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 in genere registrati nel metodo dell'app Startup.ConfigureServices .Services are typically 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' IMyDependency interfaccia definisce il WriteMessage Metodo:In the sample app, the IMyDependency interface defines the WriteMessage method:

public interface IMyDependency
{
    void 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
{
    public void WriteMessage(string message)
    {
        Console.WriteLine($"MyDependency.WriteMessage Message: {message}");
    }
}

L'app di esempio registra il IMyDependency servizio con il tipo concreto MyDependency .The sample app registers the IMyDependency service with the concrete type MyDependency. Il AddScoped metodo registra il servizio con una durata con ambito, la durata di una singola richiesta.The AddScoped method registers the service with a scoped lifetime, 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.AddScoped<IMyDependency, MyDependency>();

    services.AddRazorPages();
}

Nell'app di esempio, il IMyDependency servizio viene richiesto e usato per chiamare il WriteMessage Metodo:In the sample app, the IMyDependency service is requested and used to call the WriteMessage method:

public class Index2Model : PageModel
{
    private readonly IMyDependency _myDependency;

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

    public void OnGet()
    {
        _myDependency.WriteMessage("Index2Model.OnGet");
    }
}

Utilizzando il modello DI, il controller:By using the DI pattern, the controller:

  • Non usa il tipo concreto MyDependency , bensì solo l' IMyDependency interfaccia che implementa.Doesn't use the concrete type MyDependency, only the IMyDependency interface it implements. Questo consente di modificare facilmente l'implementazione utilizzata dal controller senza modificare il controller.That makes it easy to change the implementation that the controller uses without modifying the controller.
  • Non crea un'istanza di MyDependency , viene creata dal contenitore di.Doesn't create an instance of MyDependency, it's created by the DI container.

L'implementazione dell' IMyDependency interfaccia può essere migliorata usando l'API di registrazione incorporata:The implementation of the IMyDependency interface can be improved by using the built-in logging API:

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

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

    public void WriteMessage(string message)
    {
        _logger.LogInformation( $"MyDependency2.WriteMessage Message: {message}");
    }
}

Il ConfigureServices metodo aggiornato registra la nuova IMyDependency implementazione:The updated ConfigureServices method registers the new IMyDependency implementation:

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

    services.AddRazorPages();
}

MyDependency2 dipende da ILogger<TCategoryName> , che richiede nel costruttore.MyDependency2 depends on ILogger<TCategoryName>, which it requests in the constructor. ILogger<TCategoryName> è un servizio fornito dal Framework.ILogger<TCategoryName> is a framework-provided service.

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.

Il contenitore si risolve sfruttando ILogger<TCategoryName> i vantaggi dei tipi aperti (generici), eliminando la necessità di registrare tutti i tipi costruiti (generici).The container resolves ILogger<TCategoryName> by taking advantage of (generic) open types, eliminating the need to register every (generic) constructed type.

Nella terminologia relativa all'inserimento delle dipendenze, un servizio:In dependency injection terminology, a service:

  • È in genere un oggetto che fornisce un servizio ad altri oggetti, ad esempio il IMyDependency servizio.Is typically an object that provides a service to other objects, such as the IMyDependency service.
  • Non è correlato a un servizio Web, anche se il servizio può utilizzare un servizio Web.Is not related to a web service, although the service may use a web service.

Il Framework fornisce un sistema di registrazione affidabile.The framework provides a robust logging system. Le IMyDependency implementazioni illustrate negli esempi precedenti sono state scritte per illustrare il di base di, non per implementare la registrazione.The IMyDependency implementations shown in the preceding examples were written to demonstrate basic DI, not to implement logging. La maggior parte delle app non deve scrivere logger.Most apps shouldn't need to write loggers. Il codice seguente illustra l'uso della registrazione predefinita, che non richiede la registrazione dei servizi in ConfigureServices :The following code demonstrates using the default logging, which doesn't require any services to be registered in ConfigureServices:

public class AboutModel : PageModel
{
    private readonly ILogger _logger;

    public AboutModel(ILogger<AboutModel> logger)
    {
        _logger = logger;
    }
    
    public string Message { get; set; }

    public void OnGet()
    {
        Message = $"About page visited at {DateTime.UtcNow.ToLongTimeString()}";
        _logger.LogInformation(Message);
    }
}

Con il codice precedente non è necessario aggiornare ConfigureServices , perché la registrazione viene fornita dal Framework.Using the preceding code, there is no need to update ConfigureServices, because logging is provided by the framework.

Servizi inseriti all'avvioServices injected into Startup

I servizi possono essere inseriti nel Startup costruttore e nel Startup.Configure metodo.Services can be injected into the Startup constructor and the Startup.Configure method.

StartupQuando si utilizza l'host generico () è possibile inserire nel costruttore solo i servizi seguenti IHostBuilder :Only the following services can be injected into the Startup constructor when using the Generic Host (IHostBuilder):

Qualsiasi servizio registrato con il contenitore DI inserimento delle dipendenze può essere inserito nel Startup.Configure Metodo:Any service registered with the DI container can be injected into the Startup.Configure method:

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

Per ulteriori informazioni, vedere Avvio dell'app in ASP.NET Core e accedere alla configurazione all'avvio.For more information, see Avvio dell'app in ASP.NET Core and Access configuration in Startup.

Registrare gruppi di servizi con i metodi di estensioneRegister groups of services with extension methods

Il framework ASP.NET Core usa una convenzione per la registrazione di un gruppo di servizi correlati.The ASP.NET Core framework uses a convention for registering a group of related services. La convenzione prevede l'uso di un singolo Add{GROUP_NAME} metodo di estensione per registrare tutti i servizi richiesti da una funzionalità del Framework.The convention is to use a single Add{GROUP_NAME} extension method to register all of the services required by a framework feature. Ad esempio, il metodo <Microsoft. Extensions. DependencyInjection. MvcServiceCollectionExtensions. AddControllers> Extension registra i servizi necessari per i controller MVC.For example, the <Microsoft.Extensions.DependencyInjection.MvcServiceCollectionExtensions.AddControllers> extension method registers the services required for MVC controllers.

Il codice seguente viene generato dal Razor modello di pagine usando singoli account utente e Mostra come aggiungere altri servizi al contenitore usando i metodi di estensione AddDbContext e AddDefaultIdentity :The following code is generated by the Razor Pages template using individual user accounts and shows how to add additional services to the container using the extension methods AddDbContext and AddDefaultIdentity:

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

Si consideri il ConfigureServices metodo seguente, che registra i servizi e configura le opzioni:Consider the following ConfigureServices method, which registers services and configures options:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<PositionOptions>(
        Configuration.GetSection(PositionOptions.Position));
    services.Configure<ColorOptions>(
        Configuration.GetSection(ColorOptions.Color));

    services.AddScoped<IMyDependency, MyDependency>();
    services.AddScoped<IMyDependency2, MyDependency2>();

    services.AddRazorPages();
}

I gruppi di registrazioni correlati possono essere spostati in un metodo di estensione per registrare i servizi.Related groups of registrations can be moved to an extension method to register services. I servizi di configurazione, ad esempio, vengono aggiunti alla classe seguente:For example, the configuration services are added to the following class:

using ConfigSample.Options;
using Microsoft.Extensions.Configuration;

namespace Microsoft.Extensions.DependencyInjection
{
    public static class MyConfigServiceCollectionExtensions
    {
        public static IServiceCollection AddConfig(
             this IServiceCollection services, IConfiguration config)
        {
            services.Configure<PositionOptions>(
                config.GetSection(PositionOptions.Position));
            services.Configure<ColorOptions>(
                config.GetSection(ColorOptions.Color));

            return services;
        }
    }
}

I servizi rimanenti sono registrati in una classe simile.The remaining services are registered in a similar class. Il ConfigureServices metodo seguente usa i nuovi metodi di estensione per registrare i servizi:The following ConfigureServices method uses the new extension methods to register the services:

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

    services.AddRazorPages();
}

Nota: Ogni services.Add{GROUP_NAME} metodo di estensione aggiunge e potenzialmente configura i servizi.Note: Each services.Add{GROUP_NAME} extension method adds and potentially configures services. Ad esempio, AddControllersWithViews aggiunge i controller MVC dei servizi con le visualizzazioni require e AddRazorPages aggiunge i servizi richiesti Razor Pages.For example, AddControllersWithViews adds the services MVC controllers with views require, and AddRazorPages adds the services Razor Pages requires. È consigliabile che le app seguano questa convenzione di denominazione.We recommended that apps follow this naming 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.

Durate del servizioService lifetimes

I servizi possono essere registrati con una delle seguenti durate:Services can be registered with one of the following lifetimes:

  • TemporaneoTransient
  • Con ambitoScoped
  • SingletonSingleton

Nelle sezioni seguenti vengono descritte tutte le durate precedenti.The following sections describe each of the preceding lifetimes. Scegliere una durata appropriata per ogni servizio registrato.Choose an appropriate lifetime for each registered service.

TemporaneoTransient

I servizi con durata temporanea vengono creati ogni volta che vengono richiesti dal contenitore dei servizi.Transient lifetime services 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. Registrare servizi temporanei con AddTransient .Register transient services with AddTransient.

Nelle app che elaborano le richieste, i servizi temporanei vengono eliminati alla fine della richiesta.In apps that process requests, transient services are disposed at the end of the request.

Con ambitoScoped

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

Nelle app che elaborano le richieste, i servizi con ambito vengono eliminati alla fine della richiesta.In apps that process requests, scoped services are disposed at the end of the request.

Quando si usa Entity Framework Core, il AddDbContext metodo di estensione registra i DbContext tipi con una durata con ambito per impostazione predefinita.When using Entity Framework Core, the AddDbContext extension method registers DbContext types with a scoped lifetime by default.

Non risolvere un servizio con ambito da un singleton.Do not 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. È bene:It's fine to:

  • Risolvere un servizio singleton da un servizio con ambito o temporaneo.Resolve a singleton service from a scoped or transient service.
  • Risolvere un servizio con ambito da un altro servizio con ambito o temporaneo.Resolve a scoped service from another scoped or transient service.

Per impostazione predefinita, nell'ambiente di sviluppo la risoluzione di un servizio da un altro servizio con una durata più lunga genera un'eccezione.By default, in the development environment, resolving a service from another service with a longer lifetime throws an exception. Per ulteriori informazioni, vedere Convalida dell'ambito.For more information, see Scope validation.

Per usare i servizi con ambito nel middleware, usare uno degli approcci seguenti:To use scoped services in middleware, use one of the following approaches:

  • Inserire il servizio nel middleware o nel Invoke InvokeAsync metodo.Inject the service into the middleware's Invoke or InvokeAsync method. L'uso dell' inserimento del costruttore genera un'eccezione in fase di esecuzione perché impone il comportamento del servizio con ambito come un singleton.Using constructor injection throws a runtime exception because it forces the scoped service to behave like a singleton. Nell'esempio nella sezione relativa alle Opzioni di durata e registrazione viene illustrato l' InvokeAsync approccio.The sample in the Lifetime and registration options section demonstrates the InvokeAsync approach.
  • Usare il middleware basato su Factory.Use Factory-based middleware. Il middleware registrato con questo approccio viene attivato per ogni richiesta client (connessione), che consente di inserire i servizi con ambito nel metodo del middleware InvokeAsync .Middleware registered using this approach is activated per client request (connection), which allows scoped services to be injected into the middleware's InvokeAsync method.

Per altre informazioni, vedere Scrivere middleware di ASP.NET Core personalizzato.For more information, see Scrivere middleware di ASP.NET Core personalizzato.

SingletonSingleton

I servizi di durata singleton vengono creati:Singleton lifetime services are created either:

  • La prima volta che viene richiesto.The first time they're requested.
  • Dallo sviluppatore, quando si fornisce un'istanza di implementazione direttamente al contenitore.By the developer, when providing an implementation instance directly to the container. Questo approccio è raramente necessario.This approach is rarely needed.

Tutte le richieste successive usano la stessa istanza.Every subsequent request uses the same instance. Se l'app richiede il comportamento singleton, consentire al contenitore del servizio di gestire la durata del servizio.If the app requires singleton behavior, allow the service container to manage the service's lifetime. Non implementare il modello di progettazione singleton e fornire codice per eliminare il singleton.Don't implement the singleton design pattern and provide code to dispose of the singleton. I servizi non devono mai essere eliminati dal codice che ha risolto il servizio dal contenitore.Services should never be disposed by code that resolved the service from the container. Se un tipo o una factory viene registrata come singleton, il contenitore elimina il singleton automaticamente.If a type or factory is registered as a singleton, the container disposes the singleton automatically.

Registrare i servizi singleton con AddSingleton .Register singleton services with AddSingleton. I servizi singleton devono essere thread-safe e spesso utilizzati nei servizi senza stato.Singleton services must be thread safe and are often used in stateless services.

Nelle app che elaborano le richieste, i servizi Singleton vengono eliminati quando il ServiceProvider viene eliminato alla chiusura dell'applicazione.In apps that process requests, singleton services are disposed when the ServiceProvider is disposed on application shutdown. Poiché la memoria non viene rilasciata fino a quando l'app non viene arrestata, prendere in considerazione l'uso della memoria con un servizio singleton.Because memory is not released until the app is shut down, consider memory use with a singleton service.

Avviso

Non risolvere un servizio con ambito da un singleton.Do not 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. È possibile risolvere un servizio singleton da un servizio con ambito o temporaneo.It's fine to resolve a singleton service from a scoped or transient service.

Metodi di registrazione del servizioService registration methods

Il Framework fornisce metodi di estensione per la registrazione del servizio utili in scenari specifici:The framework provides service registration extension methods that are useful in specific scenarios:

MetodoMethod AutomaticoAutomatic
objectobject
eliminazionedisposal
Più elementiMultiple
implementazioniimplementations
Pass argsPass args
Add{LIFETIME}<{SERVICE}, {IMPLEMENTATION}>()
Esempio:Example:
services.AddSingleton<IMyDep, MyDep>();
Yes Yes NoNo
Add{LIFETIME}<{SERVICE}>(sp => new {IMPLEMENTATION})
Esempi:Examples:
services.AddSingleton<IMyDep>(sp => new MyDep());
services.AddSingleton<IMyDep>(sp => new MyDep(99));
Yes Yes Yes
Add{LIFETIME}<{IMPLEMENTATION}>()
Esempio:Example:
services.AddSingleton<MyDep>();
Yes NoNo NoNo
AddSingleton<{SERVICE}>(new {IMPLEMENTATION})
Esempi:Examples:
services.AddSingleton<IMyDep>(new MyDep());
services.AddSingleton<IMyDep>(new MyDep(99));
NoNo Yes Yes
AddSingleton(new {IMPLEMENTATION})
Esempi:Examples:
services.AddSingleton(new MyDep());
services.AddSingleton(new MyDep(99));
NoNo NoNo Yes

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. Quando si simulano i tipi per il test, è comune usare più implementazioni.It's common to use multiple implementations when mocking types for testing.

Il Framework fornisce anche TryAdd{LIFETIME} metodi di estensione, che registrano il servizio solo se non è già stata registrata un'implementazione.The framework also provides TryAdd{LIFETIME} extension methods, which register the service only if there isn't already an implementation registered.

Nell'esempio seguente, la chiamata a AddSingleton Registra MyDependency come implementazione per IMyDependency .In the following example, the call to AddSingleton registers MyDependency as an implementation for IMyDependency. La chiamata a TryAddSingleton non ha alcun effetto perché IMyDependency dispone già di un'implementazione registrata:The call to TryAddSingleton 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.The TryAddEnumerable(ServiceDescriptor) methods register the service only 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}>. Quando si registrano i servizi, lo sviluppatore deve aggiungere un'istanza se uno degli stessi tipi non è già stato aggiunto.When registering services, the developer should add an instance if one of the same type hasn't already been added. In genere, gli autori TryAddEnumerable di librerie utilizzano per evitare la registrazione di più copie di un'implementazione nel contenitore.Generally, library authors use TryAddEnumerable to avoid registering multiple copies of an implementation in the container.

Nell'esempio seguente, la prima chiamata a viene TryAddEnumerable registrata MyDependency come implementazione per IMyDependency1 .In the following example, the first call to TryAddEnumerable registers MyDependency as an implementation for IMyDependency1. La seconda chiamata esegue la registrazione MyDependency per IMyDependency2 .The second call registers MyDependency for IMyDependency2. La terza chiamata non ha alcun effetto perché IMyDependency1 dispone già di un'implementazione registrata di MyDependency :The third call has no effect because IMyDependency1 already has a registered implementation of MyDependency:

public interface IMyDependency1 { }
public interface IMyDependency2 { }

public class MyDependency : IMyDependency1, IMyDependency2 { }

services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyDependency1, MyDependency>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyDependency2, MyDependency>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyDependency1, MyDependency>());

La registrazione del servizio è in genere indipendente dall'ordine, tranne quando si registrano più implementazioni dello stesso tipo.Service registration is generally order independent except when registering multiple implementations of the same type.

IServiceCollection è una raccolta di ServiceDescriptor oggetti.IServiceCollection is a collection of ServiceDescriptor objects. Nell'esempio seguente viene illustrato come registrare un servizio creando e aggiungendo ServiceDescriptor :The following example shows how to register a service by creating and adding a ServiceDescriptor:

var myKey = Configuration["MyKey"];
var descriptor = new ServiceDescriptor(
    typeof(IMyDependency),
    sp => new MyDependency5(myKey),
    ServiceLifetime.Transient);

services.Add(descriptor);

I Add{LIFETIME} metodi predefiniti utilizzano lo stesso approccio.The built-in Add{LIFETIME} methods use the same approach. Vedere ad esempio il codice sorgente di AddScoped.For example, see the AddScoped source code.

Comportamento dell'inserimento del costruttoreConstructor injection behavior

I servizi possono essere risolti tramite:Services can be resolved by using:

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

Per impostazione predefinita, i contesti di Entity Framework vengono aggiunti al contenitore del servizio usando la durata con ambito poiché le operazioni del database dell'app Web sono in genere limitate all'ambito della richiesta client.By default, Entity Framework contexts are added to the service container using the scoped lifetime because web app database operations are normally scoped to the client request. Per usare una durata diversa, specificare la durata usando un AddDbContext Overload.To use a different lifetime, specify the lifetime by using an AddDbContext overload. I servizi di una determinata durata non devono utilizzare un contesto di database con una durata minore della durata del servizio.Services of a given lifetime shouldn't use a database context with a lifetime that's shorter than the service's lifetime.

Opzioni di durata e di registrazioneLifetime and registration options

Per dimostrare la differenza tra le durate dei servizi e le relative opzioni di registrazione, prendere in considerazione le interfacce seguenti che rappresentano un'attività come operazione con un identificatore OperationId .To demonstrate the difference between service lifetimes and their registration options, consider the following interfaces that represent a task as an operation with an identifier, OperationId. A seconda del modo in cui viene configurata la durata del servizio di un'operazione per le interfacce seguenti, il contenitore fornisce le stesse istanze o diverse del servizio quando richiesto da una classe:Depending on how the lifetime of an operation's service is configured for the following interfaces, the container provides either the same or different instances of the service when requested by a class:

public interface IOperation
{
    string OperationId { get; }
}

public interface IOperationTransient : IOperation { }
public interface IOperationScoped : IOperation { }
public interface IOperationSingleton : IOperation { }

La Operation classe seguente implementa tutte le interfacce precedenti.The following Operation class implements all of the preceding interfaces. Il Operation costruttore genera un GUID e archivia gli ultimi 4 caratteri nella OperationId proprietà:The Operation constructor generates a GUID and stores the last 4 characters in the OperationId property:

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

    public string OperationId { get; }
}

Il Startup.ConfigureServices metodo crea più registrazioni della Operation classe in base alle durate denominate:The Startup.ConfigureServices method creates multiple registrations of the Operation class according to the named lifetimes:

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

    services.AddRazorPages();
}

L'app di esempio illustra le durate degli oggetti all'interno e tra le richieste.The sample app demonstrates object lifetimes both within and between requests. Il IndexModel e il middleware richiedono ogni tipo di IOperation tipo e registrano OperationId per ogni tipo:The IndexModel and the middleware request each kind of IOperation type and log the OperationId for each:

public class IndexModel : PageModel
{
    private readonly ILogger _logger;
    private readonly IOperationTransient _transientOperation;
    private readonly IOperationSingleton _singletonOperation;
    private readonly IOperationScoped _scopedOperation;

    public IndexModel(ILogger<IndexModel> logger,
                      IOperationTransient transientOperation,
                      IOperationScoped scopedOperation,
                      IOperationSingleton singletonOperation)
    {
        _logger = logger;
        _transientOperation = transientOperation;
        _scopedOperation    = scopedOperation;
        _singletonOperation = singletonOperation;
    }

    public void  OnGet()
    {
        _logger.LogInformation("Transient: " + _transientOperation.OperationId);
        _logger.LogInformation("Scoped: "    + _scopedOperation.OperationId);
        _logger.LogInformation("Singleton: " + _singletonOperation.OperationId);
    }
}

Analogamente a IndexModel , il middleware risolve gli stessi servizi:Similar to the IndexModel, the middleware resolves the same services:

public class MyMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;

    private readonly IOperationTransient _transientOperation;
    private readonly IOperationSingleton _singletonOperation;

    public MyMiddleware(RequestDelegate next, ILogger<MyMiddleware> logger,
        IOperationTransient transientOperation,
        IOperationSingleton singletonOperation)
    {
        _logger = logger;
        _transientOperation = transientOperation;
        _singletonOperation = singletonOperation;
        _next = next;
    }

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

        await _next(context);
    }
}

public static class MyMiddlewareExtensions
{
    public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<MyMiddleware>();
    }
}

I servizi con ambito devono essere risolti nel InvokeAsync Metodo:Scoped services must be resolved in the InvokeAsync method:

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

    await _next(context);
}

L'output del logger Mostra:The logger output shows:

  • Gli oggetti temporanei sono sempre diversi.Transient objects are always different. Il OperationId valore temporaneo è diverso in IndexModel e nel middleware.The transient OperationId value is different in the IndexModel and in the middleware.
  • Gli oggetti con ambito sono gli stessi per ogni richiesta, ma sono diversi in ogni richiesta.Scoped objects are the same for each request but different across each request.
  • Gli oggetti singleton sono gli stessi per ogni richiesta.Singleton objects are the same for every request.

Per ridurre l'output di registrazione, impostare "Logging: LogLevel: Microsoft: Error" nella appsettings.Development.jssu file:To reduce the logging output, set "Logging:LogLevel:Microsoft:Error" in the appsettings.Development.json file:

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

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.

Nell'esempio seguente viene illustrato come accedere al servizio con ambito IMyDependency e come chiamare il relativo WriteMessage metodo in Program.Main :The following example shows how to access the scoped IMyDependency service and call its WriteMessage method in Program.Main:

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

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

            try
            {
                var myDependency = services.GetRequiredService<IMyDependency>();
                myDependency.WriteMessage("Call services from main");
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "An error occurred.");
            }
        }

        host.Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

Convalida dell'ambitoScope validation

Quando l'app viene eseguita nell' ambiente di sviluppo e chiama CreateDefaultBuilder per compilare l'host, il provider di servizi predefinito esegue i controlli per verificare che:When the app runs in the Development environment and calls CreateDefaultBuilder to build the host, the default service provider performs checks to verify that:

  • I servizi con ambito non vengono risolti dal provider di servizi radice.Scoped services aren't resolved from the root service provider.
  • I servizi con ambito non vengono inseriti in singleton.Scoped services aren't 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 quando il provider inizia con l'app e viene eliminato quando l'app viene arrestata.The root service provider's lifetime corresponds to the app'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 nel contenitore radice viene creato un servizio con ambito, la durata del servizio viene effettivamente promossa a Singleton perché viene eliminata dal contenitore radice solo quando l'app viene arrestata.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 the app shuts 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 Convalida dell'ambito.For more information, see Scope validation.

Servizi di richiestaRequest Services

I servizi disponibili all'interno di una richiesta di ASP.NET Core vengono esposti tramite la raccolta HttpContext. RequestServices .The services available within an ASP.NET Core request are exposed through the HttpContext.RequestServices collection. Quando i servizi vengono richiesti dall'interno di una richiesta, i servizi e le relative dipendenze vengono risolti dalla RequestServices raccolta.When services are requested from inside of a request, the services and their dependencies are resolved from the RequestServices collection.

Il Framework crea un ambito per ogni richiesta ed RequestServices espone il provider di servizi con ambito.The framework creates a scope per request and RequestServices exposes the scoped service provider. Tutti i servizi con ambito sono validi fino a quando la richiesta è attiva.All scoped services are valid for as long as the request is active.

Nota

Preferisce richiedere le dipendenze come parametri del costruttore per la risoluzione dei servizi dalla RequestServices raccolta.Prefer requesting dependencies as constructor parameters to resolving services from the RequestServices collection. In questo modo, le classi risultano più facili da testare.This results in classes that are easier to test.

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

Durante la progettazione di servizi per l'inserimento di dipendenze:When designing services for dependency injection:

  • Evitare membri e classi statiche con stato.Avoid stateful, static classes and members. Evitare di creare lo stato globale progettando le app in modo da usare i servizi singleton.Avoid creating global state by designing apps to use singleton services instead.
  • 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.
  • Semplifica la testing dei servizi.Make services small, well-factored, and easily tested.

Se una classe dispone di molte dipendenze inserite, potrebbe essere un segno che la classe ha troppe responsabilità e viola il principio di singola responsabilità (SRP).If a class has a lot of injected dependencies, it might be a sign that the class has too many responsibilities and violates the Single Responsibility Principle (SRP). Tentare di effettuare il refactoring della classe spostando alcune delle responsabilità in nuove classi.Attempt to refactor the class by moving some of its responsibilities into new classes. Tenere presente che Razor le classi del modello della pagina delle pagine e le classi controller MVC devono concentrarsi sulle problematiche dell'interfaccia utente.Keep in mind that Razor Pages page model classes and MVC controller classes should focus on UI 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. I servizi risolti dal contenitore non devono mai essere eliminati dallo sviluppatore.Services resolved from the container should never be disposed by the developer. Se un tipo o una factory viene registrata come singleton, il contenitore elimina il singleton automaticamente.If a type or factory is registered as a singleton, the container disposes the singleton automatically.

Nell'esempio seguente i servizi vengono creati dal contenitore del servizio ed eliminati automaticamente:In the following example, the services are created by the service container and disposed automatically:

public class Service1 : IDisposable
{
    private bool _disposed;

    public void Write(string message)
    {
        Console.WriteLine($"Service1: {message}");
    }

    public void Dispose()
    {
        if (_disposed)
            return;

        Console.WriteLine("Service1.Dispose");
        _disposed = true;
    }
}

public class Service2 : IDisposable
{
    private bool _disposed;

    public void Write(string message)
    {
        Console.WriteLine($"Service2: {message}");
    }

    public void Dispose()
    {
        if (_disposed)
            return;

        Console.WriteLine("Service2.Dispose");
        _disposed = true;
    }
}

public interface IService3
{
    public void Write(string message);
}

public class Service3 : IService3, IDisposable
{
    private bool _disposed;

    public Service3(string myKey)
    {
        MyKey = myKey;
    }

    public string MyKey { get; }

    public void Write(string message)
    {
        Console.WriteLine($"Service3: {message}, MyKey = {MyKey}");
    }

    public void Dispose()
    {
        if (_disposed)
            return;

        Console.WriteLine("Service3.Dispose");
        _disposed = true;
    }
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<Service1>();
    services.AddSingleton<Service2>();
    
    var myKey = Configuration["MyKey"];
    services.AddSingleton<IService3>(sp => new Service3(myKey));

    services.AddRazorPages();
}
public class IndexModel : PageModel
{
    private readonly Service1 _service1;
    private readonly Service2 _service2;
    private readonly IService3 _service3;

    public IndexModel(Service1 service1, Service2 service2, IService3 service3)
    {
        _service1 = service1;
        _service2 = service2;
        _service3 = service3;
    }

    public void OnGet()
    {
        _service1.Write("IndexModel.OnGet");
        _service2.Write("IndexModel.OnGet");
        _service3.Write("IndexModel.OnGet");
    }
}

La console di debug Visualizza il seguente output dopo ogni aggiornamento della pagina di indice:The debug console shows the following output after each refresh of the Index page:

Service1: IndexModel.OnGet
Service2: IndexModel.OnGet
Service3: IndexModel.OnGet
Service1.Dispose

Servizi non creati dal contenitore del servizioServices not created by the service container

Osservare il codice seguente:Consider the following code:

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

    services.AddRazorPages();
}

Nel codice precedente:In the preceding code:

  • Le istanze del servizio non vengono create dal contenitore dei servizi.The service instances aren't created by the service container.
  • Il Framework non elimina automaticamente i servizi.The framework doesn't dispose of the services automatically.
  • Lo sviluppatore è responsabile dell'eliminazione dei servizi.The developer is responsible for disposing the services.

Linee guida IDisposable per istanze temporanee e condiviseIDisposable guidance for Transient and shared instances

Durata temporanea e limitataTransient, limited lifetime

ScenarioScenario

L'app richiede un' IDisposable istanza con una durata temporanea per uno degli scenari seguenti:The app requires an IDisposable instance with a transient lifetime for either of the following scenarios:

  • L'istanza viene risolta nell'ambito radice (contenitore radice).The instance is resolved in the root scope (root container).
  • L'istanza deve essere eliminata prima della fine dell'ambito.The instance should be disposed before the scope ends.

SoluzioneSolution

Usare il modello factory per creare un'istanza all'esterno dell'ambito padre.Use the factory pattern to create an instance outside of the parent scope. In questa situazione, in genere l'app dispone di un Create metodo che chiama direttamente il costruttore del tipo finale.In this situation, the app would generally have a Create method that calls the final type's constructor directly. Se il tipo finale ha altre dipendenze, la factory può:If the final type has other dependencies, the factory can:

Istanza condivisa, durata limitataShared instance, limited lifetime

ScenarioScenario

L'app richiede un' IDisposable istanza condivisa tra più servizi, ma l' IDisposable istanza deve avere una durata limitata.The app requires a shared IDisposable instance across multiple services, but the IDisposable instance should have a limited lifetime.

SoluzioneSolution

Registrare l'istanza con una durata con ambito.Register the instance with a scoped lifetime. Utilizzare IServiceScopeFactory.CreateScope per creare un nuovo oggetto IServiceScope .Use IServiceScopeFactory.CreateScope to create a new IServiceScope. Usare l'ambito IServiceProvider per ottenere i servizi richiesti.Use the scope's IServiceProvider to get required services. Eliminare l'ambito quando non è più necessario.Dispose the scope when it's no longer needed.

Linee guida generali sull'IDisposableGeneral IDisposable guidelines

  • Non registrare IDisposable le istanze con una durata temporanea.Don't register IDisposable instances with a transient lifetime. Usare invece il modello Factory.Use the factory pattern instead.
  • Non risolvere IDisposable le istanze con una durata temporanea o con ambito nell'ambito radice.Don't resolve IDisposable instances with a transient or scoped lifetime in the root scope. L'unica eccezione è rappresentata dal caso in cui l'app crea/ricrea ed Elimina IServiceProvider , ma questo non è un modello ideale.The only exception to this is if the app creates/recreates and disposes IServiceProvider, but this isn't an ideal pattern.
  • Per IDisposable la ricezione di una dipendenza tramite di non è necessario che il ricevitore implementi IDisposable se stesso.Receiving an IDisposable dependency via DI doesn't require that the receiver implement IDisposable itself. Il ricevitore della IDisposable dipendenza non deve chiamare Dispose su tale dipendenza.The receiver of the IDisposable dependency shouldn't call Dispose on that dependency.
  • Utilizzare gli ambiti per controllare la durata dei servizi.Use scopes to control the lifetimes of services. Gli ambiti non sono gerarchici e non esiste alcuna connessione speciale tra gli ambiti.Scopes aren't hierarchical, and there's no special connection among scopes.

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. È consigliabile usare il contenitore predefinito a meno che non sia necessaria una funzionalità specifica che non supporta, ad esempio:We recommend using the built-in container unless you need a specific feature that it 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
  • Registrazione basata sulle convenzioniConvention-based registration

Con ASP.NET Core app è possibile usare i contenitori di terze parti seguenti:The following third-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 presenta una dipendenza da un servizio temporaneo, il servizio temporaneo potrebbe richiedere anche thread safety a seconda del modo in cui viene utilizzato dal singleton.If a singleton service has a dependency on a transient service, the transient service may also require thread safety depending on how it's used by the singleton.

Il metodo factory di un singolo servizio, ad esempio il secondo argomento di AddSingleton <TService> (IServiceCollection, Func <IServiceProvider,TService> ), non deve essere 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. Analogamente a un static Costruttore Type (), è garantita la chiamata solo una volta da un singolo thread.Like a type (static) constructor, it's guaranteed to be called only once by a single thread.

ConsigliRecommendations

  • async/await la Task risoluzione del servizio basata su non è supportata.async/await and Task based service resolution isn't supported. Poiché C# non supporta costruttori asincroni, utilizzare i metodi asincroni dopo la risoluzione sincrona del servizio.Because C# doesn't support asynchronous constructors, 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. Allo stesso modo, evitare gli oggetti "titolare dei dati" che esistono solo per consentire l'accesso a un altro oggetto.Similarly, avoid "data holder" objects that only exist to allow access to another 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.Avoid static access to services. Ad esempio, evitare di acquisire IApplicationBuilder. ApplicationServices come proprietà o campo statico da usare altrove.For example, avoid capturing IApplicationBuilder.ApplicationServices as a static field or property for use elsewhere.

  • Mantieni le fabbriche veloci e sincrone.Keep DI factories fast and synchronous.

  • 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:

    Codice errato

    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).

  • Evitare chiamate a BuildServiceProvider in ConfigureServices .Avoid calls to BuildServiceProvider in ConfigureServices. BuildServiceProviderLa chiamata in genere si verifica quando lo sviluppatore desidera risolvere un servizio in ConfigureServices .Calling BuildServiceProvider typically happens when the developer wants to resolve a service in ConfigureServices. Si consideri, ad esempio, il caso in cui l'oggetto LoginPath viene caricato dalla configurazione.For example, consider the case where the LoginPath is loaded from configuration. Evitare l'approccio seguente:Avoid the following approach:

    codice errato durante la chiamata di BuildServiceProvider

    Nell'immagine precedente, selezionando la linea ondulata verde in services.BuildServiceProvider viene visualizzato l'avviso ASP0000 seguente:In the preceding image, selecting the green wavy line under services.BuildServiceProvider shows the following ASP0000 warning:

    ASP0000 la chiamata a' BuildServiceProvider ' dal codice dell'applicazione comporta una copia aggiuntiva dei servizi singleton creati.ASP0000 Calling 'BuildServiceProvider' from application code results in an additional copy of singleton services being created. Prendere in considerazione alternative come la dipendenza che inserisce i servizi come parametri per "Configure".Consider alternatives such as dependency injecting services as parameters to 'Configure'.

    BuildServiceProviderLa chiamata di crea un secondo contenitore, che può creare singleton incompleti e causare riferimenti a oggetti grafici tra più contenitori.Calling BuildServiceProvider creates a second container, which can create torn singletons and cause references to object graphs across multiple containers.

    Un modo corretto per ottenere LoginPath consiste nell'usare il supporto incorporato del modello di opzioni per l'inserimento delle dipendenze:A correct way to get LoginPath is to use the options pattern's built-in support for DI:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddCookie();
    
        services.AddOptions<CookieAuthenticationOptions>(
                            CookieAuthenticationDefaults.AuthenticationScheme)
            .Configure<IMyService>((options, myService) =>
            {
                options.LoginPath = myService.GetLoginPath();
            });
    
        services.AddRazorPages();
    }
    
  • I servizi temporanei Disposable vengono acquisiti dal contenitore per l'eliminazione.Disposable transient services are captured by the container for disposal. Questa operazione può comportare una perdita di memoria se risolta dal contenitore di primo livello.This can turn into a memory leak if resolved from the top level container.

  • Abilitare la convalida dell'ambito per assicurarsi che l'app non disponga di singleton che acquisiscono i servizi con ambito.Enable scope validation to make sure the app doesn't have singletons that capture scoped services. Per ulteriori informazioni, vedere Convalida dell'ambito.For more information, see Scope validation.

È 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 rari, soprattutto 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.

Orchard Core è un Framework di applicazione per la creazione di applicazioni modulari multi-tenant in ASP.NET Core.Orchard Core is an application framework for building modular, multi-tenant applications on ASP.NET Core. Per ulteriori informazioni, vedere la documentazione di Orchard Core.For more information, see the Orchard Core Documentation.

Vedere gli esempi di Orchard Core per esempi di come creare app modulari e multi-tenant usando solo il Framework Orchard core senza le funzionalità specifiche di CMS.See the Orchard Core samples for examples of how to build modular and multi-tenant apps using just the Orchard Core Framework without any of its CMS-specific features.

Servizi forniti dal frameworkFramework-provided services

Il Startup.ConfigureServices metodo registra i servizi usati dall'app, incluse le funzionalità della piattaforma, ad esempio Entity Framework Core e ASP.NET Core MVC.The Startup.ConfigureServices method registers services that the app uses, including platform features, such as Entity Framework Core and ASP.NET Core MVC. Inizialmente, l'oggetto IServiceCollection fornito a 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. Per le app basate sui modelli di ASP.NET Core, il Framework registra più di 250 servizi.For apps based on the ASP.NET Core templates, the framework registers more than 250 services.

La tabella seguente elenca un piccolo esempio di questi servizi registrati dal Framework:The following table lists a small sample of these framework-registered services:

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

Risorse aggiuntiveAdditional resources

Di Steve Smith, Scott Addiee Brandon DahlerBy Steve Smith, Scott Addie, and Brandon Dahler

ASP.NET Core supporta lo schema progettuale di software per l'inserimento di dipendenze, una tecnica per ottenere l'IoC (Inversion of Control) tra le classi e le 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);
}

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

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 registrazione delle astrazioni, 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<>), typeof(Logger<>));

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.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 le pagine dei servizi Razor e MVC require.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.");
    }
}

Servizi inseriti all'avvioServices injected into Startup

StartupQuando si utilizza l'host generico () è possibile inserire nel costruttore solo i tipi di servizio seguenti IHostBuilder :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 altre 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 Startup.ConfigureServices metodo è 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, l'oggetto IServiceCollection fornito a 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
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 servizi aggiuntivi 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 del servizioService 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.

Nelle app che elaborano le richieste, i servizi temporanei vengono eliminati alla fine della richiesta.In apps that process requests, transient services are disposed at the end of the request.

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).

Nelle app che elaborano le richieste, i servizi con ambito vengono eliminati alla fine della richiesta.In apps that process requests, scoped services are disposed at the end of the request.

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. Non inserire tramite il Costruttore Injection perché impone al servizio di comportarsi come un singleton.Don't inject via constructor injection because it forces the service to behave like a singleton. Per altre 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.

Nelle app che elaborano le richieste, i servizi Singleton vengono eliminati quando il ServiceProvider viene eliminato alla chiusura dell'app.In apps that process requests, singleton services are disposed when the ServiceProvider is disposed at app shutdown.

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
Più elementiMultiple
implementazioniimplementations
Pass argsPass args
Add{LIFETIME}<{SERVICE}, {IMPLEMENTATION}>()
Esempio:Example:
services.AddSingleton<IMyDep, MyDep>();
Yes Yes NoNo
Add{LIFETIME}<{SERVICE}>(sp => new {IMPLEMENTATION})
Esempi:Examples:
services.AddSingleton<IMyDep>(sp => new MyDep());
services.AddSingleton<IMyDep>(sp => new MyDep("A string!"));
Yes Yes Yes
Add{LIFETIME}<{IMPLEMENTATION}>()
Esempio:Example:
services.AddSingleton<MyDep>();
Yes NoNo NoNo
AddSingleton<{SERVICE}>(new {IMPLEMENTATION})
Esempi:Examples:
services.AddSingleton<IMyDep>(new MyDep());
services.AddSingleton<IMyDep>(new MyDep("A string!"));
NoNo Yes Yes
AddSingleton(new {IMPLEMENTATION})
Esempi:Examples:
services.AddSingleton(new MyDep());
services.AddSingleton(new MyDep("A string!"));
NoNo NoNo Yes

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
  • ActivatorUtilities: 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. L'ambito della durata predefinita è se una durata non è specificata da un overload AddDbContext <TContext> durante la registrazione del 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
{
}

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

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

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

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 per ogni richiesta, indipendentemente dal fatto che un' Operation istanza venga fornita 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.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 altre 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 Razor le classi del modello della pagina delle pagine e le classi controller MVC devono concentrarsi 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.

Nell'esempio seguente i servizi vengono creati dal contenitore del servizio ed eliminati automaticamente:In the following example, the services are created by the service container and disposed automatically:

public class Service1 : IDisposable {}
public class Service2 : IDisposable {}

public interface IService3 {}
public class Service3 : IService3, IDisposable {}

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<Service1>();
    services.AddSingleton<Service2>();
    services.AddSingleton<IService3>(sp => new Service3());
}

Nell'esempio seguente:In the following example:

  • Le istanze del servizio non vengono create dal contenitore dei servizi.The service instances aren't created by the service container.
  • Le durate di servizio previste non sono note dal Framework.The intended service lifetimes aren't known by the framework.
  • Il Framework non elimina automaticamente i servizi.The framework doesn't dispose of the services automatically.
  • Se i servizi non vengono eliminati in modo esplicito nel codice dello sviluppatore, vengono mantenuti fino a quando l'app non viene chiusa.If the services aren't explicitly disposed in developer code, they persist until the app shuts down.
public class Service1 : IDisposable {}
public class Service2 : IDisposable {}

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

Linee guida IDisposable per istanze temporanee e condiviseIDisposable guidance for Transient and shared instances

Durata temporanea e limitataTransient, limited lifetime

ScenarioScenario

L'app richiede un' IDisposable istanza con una durata temporanea per uno degli scenari seguenti:The app requires an IDisposable instance with a transient lifetime for either of the following scenarios:

  • L'istanza viene risolta nell'ambito radice.The instance is resolved in the root scope.
  • L'istanza deve essere eliminata prima della fine dell'ambito.The instance should be disposed before the scope ends.

SoluzioneSolution

Usare il modello factory per creare un'istanza all'esterno dell'ambito padre.Use the factory pattern to create an instance outside of the parent scope. In questa situazione, in genere l'app dispone di un Create metodo che chiama direttamente il costruttore del tipo finale.In this situation, the app would generally have a Create method that calls the final type's constructor directly. Se il tipo finale ha altre dipendenze, la factory può:If the final type has other dependencies, the factory can:

Istanza condivisa, durata limitataShared Instance, limited lifetime

ScenarioScenario

L'app richiede un' IDisposable istanza condivisa tra più servizi, ma IDisposable deve avere una durata limitata.The app requires a shared IDisposable instance across multiple services, but the IDisposable should have a limited lifetime.

SoluzioneSolution

Registrare l'istanza con una durata con ambito.Register the instance with a Scoped lifetime. Usare IServiceScopeFactory.CreateScope per avviare e creare un nuovo oggetto IServiceScope .Use IServiceScopeFactory.CreateScope to start and create a new IServiceScope. Usare l'ambito IServiceProvider per ottenere i servizi richiesti.Use the scope's IServiceProvider to get required services. Eliminare l'ambito quando la durata deve terminare.Dispose the scope when the lifetime should end.

Linee guida generaliGeneral Guidelines

  • Non registrare IDisposable istanze con un ambito temporaneo.Don't register IDisposable instances with a Transient scope. Usare invece il modello Factory.Use the factory pattern instead.
  • Non risolvere istanze temporanee o IDisposable con ambito nell'ambito radice.Don't resolve Transient or Scoped IDisposable instances in the root scope. L'unica eccezione generale si verifica quando l'app crea/ricrea ed Elimina IServiceProvider , che non è un modello ideale.The only general exception is when the app creates/recreates and disposes the IServiceProvider, which isn't an ideal pattern.
  • Per IDisposable la ricezione di una dipendenza tramite di non è necessario che il ricevitore implementi IDisposable se stesso.Receiving an IDisposable dependency via DI doesn't require that the receiver implement IDisposable itself. Il ricevitore della IDisposable dipendenza non deve chiamare Dispose su tale dipendenza.The receiver of the IDisposable dependency shouldn't call Dispose on that dependency.
  • Gli ambiti devono essere usati per controllare la durata dei servizi.Scopes should be used to control lifetimes of services. Gli ambiti non sono gerarchici e non esiste alcuna connessione speciale tra gli ambiti.Scopes aren't hierarchical, and there's no special connection among scopes.

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
  • Registrazione basata sulle convenzioniConvention-based registration

Con ASP.NET Core app è possibile usare i contenitori di terze parti seguenti:The following third-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.

Il metodo factory di un singolo servizio, ad esempio il secondo argomento di AddSingleton <TService> (IServiceCollection, Func <IServiceProvider,TService> ), non deve essere 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.

ConsigliRecommendations

  • 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.Avoid static access to services. Ad esempio, evitare di digitare in modo statico IApplicationBuilder. ApplicationServices per l'uso altrove.For example, avoid statically-typing IApplicationBuilder.ApplicationServices for use elsewhere.

  • Evitare di usare il modello del localizzatore di servizi, che combina inversione delle strategie di controllo .Avoid using the service locator pattern, which mixes Inversion of Control strategies.

    • Non richiamare GetService per ottenere un'istanza del servizio quando si può usare invece di: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;
      
              ...
          }
      }
      
  • Evitare di inserire una factory che risolve le dipendenze in fase di esecuzione usando GetService .Avoid injecting a factory that resolves dependencies at runtime using GetService.

  • 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 rari, soprattutto 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