Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
In questo quickstart, si crea un'app console .NET che crea manualmente un oggetto ServiceCollection e un corrispondente ServiceProvider. Imparerai come registrare i servizi e risolverli utilizzando l'inserimento delle dipendenze (DI). Questo articolo usa il pacchetto NuGet Microsoft.Extensions.DependencyMakection per illustrare le nozioni di base dell'inserimento delle dipendenze in .NET.
Annotazioni
Questo articolo non sfrutta le funzionalità dell'host generico . Per una guida più completa, vedere Usare l'inserimento delle dipendenze in .NET.
Get started
Per iniziare, creare una nuova applicazione console .NET denominata DI.Basics. In Visual Studio scegliere File > Nuovo > Progetto o usare la CLI di .NET, immettere dotnet new console.
Aggiungere quindi un riferimento al pacchetto al file di progetto Microsoft.Extensions.DependencyInjection. Dopo aver aggiunto il pacchetto, verificare che il progetto sia simile al codice XML seguente del file DI.Basics.csproj :
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.5" />
</ItemGroup>
</Project>
Fondamenti di iniezione delle dipendenze
L'inserimento delle dipendenze è un modello di progettazione che è possibile usare per rimuovere le dipendenze hardcoded e rendere l'applicazione più gestibile e testabile. L'inserimento delle dipendenze è una tecnica per ottenere l'inversione del controllo (IoC) tra le classi e le relative dipendenze.
Il pacchetto NuGet Microsoft.Extensions.DependencyInjection.Abstractions definisce le astrazioni per l'inserimento delle dipendenze in .NET.
- IServiceCollection: definisce un contratto per una raccolta di descrittori di servizio.
- IServiceProvider: definisce un meccanismo per il recupero di un oggetto servizio.
- ServiceDescriptor: descrive un servizio con il tipo di servizio, l'implementazione e la durata.
In .NET, il dependency injection viene gestito aggiungendo i servizi e configurandoli in IServiceCollection. Dopo aver registrato i servizi, chiamare il metodo BuildServiceProvider per creare un'istanza di IServiceProvider.
IServiceProvider agisce come contenitore per tutti i servizi registrati e viene utilizzato per risolvere i servizi.
Creare servizi di esempio
Non tutti i servizi vengono creati equamente. Alcuni servizi richiedono una nuova istanza ogni volta che vengono recuperati dal contenitore dei servizi (transitorio), mentre altri devono essere condivisi tra le richieste (con ambito limitato) o per l'intera durata dell'app (singleton). Per altre informazioni sulla durata del servizio, vedere Durata del servizio.
Analogamente, alcuni servizi espongono solo un tipo concreto, mentre altri sono espressi come contratto tra un'interfaccia e un tipo di implementazione. È possibile creare diverse varianti di servizi per illustrare questi concetti.
Creare un nuovo file C# denominato IConsole.cs e aggiungere il codice seguente:
public interface IConsole
{
void WriteLine(string message);
}
Questo file definisce un'interfaccia IConsole che espone un singolo metodo, WriteLine. Creare quindi un nuovo file C# denominato DefaultConsole.cs e aggiungere il codice seguente:
internal sealed class DefaultConsole : IConsole
{
public bool IsEnabled { get; set; } = true;
void IConsole.WriteLine(string message)
{
if (IsEnabled is false)
{
return;
}
Console.WriteLine(message);
}
}
Il codice precedente rappresenta l'implementazione predefinita dell'interfaccia IConsole . Il WriteLine metodo scrive in modo condizionale nella console in base alla IsEnabled proprietà .
Suggerimento
La denominazione di un'implementazione è una scelta che il team di sviluppo deve concordare. Il Default prefisso è una convenzione comune per indicare un'implementazione predefinita di un'interfaccia, ma non è obbligatoria.
Creare quindi un file IGreetingService.cs e aggiungere il codice C# seguente:
public interface IGreetingService
{
string Greet(string name);
}
Aggiungere quindi un nuovo file C# denominato DefaultGreetingService.cs e aggiungere il codice seguente:
internal sealed class DefaultGreetingService(
IConsole console) : IGreetingService
{
public string Greet(string name)
{
var greeting = $"Hello, {name}!";
console.WriteLine(greeting);
return greeting;
}
}
Il codice precedente rappresenta l'implementazione predefinita dell'interfaccia IGreetingService . L'implementazione del servizio richiede un IConsole come parametro del costruttore principale. Metodo Greet:
- Crea un
greetingin base alname. - Chiama il metodo
WriteLinesull'istanzaIConsole. - Restituisce
greetingal chiamante.
La classe DefaultGreetingService dimostra che è possibile seal implementazioni di servizio per impedire l'ereditarietà e usare internal per limitare l'accesso all'assembly.
L'ultimo servizio da creare è il file FarewellService.cs . Aggiungere il codice C# seguente prima di continuare:
public class FarewellService(IConsole console)
{
public string SayGoodbye(string name)
{
var farewell = $"Goodbye, {name}!";
console.WriteLine(farewell);
return farewell;
}
}
Rappresenta FarewellService un tipo concreto, non un'interfaccia. È necessario dichiararlo come public per renderlo accessibile ai consumatori. A differenza di altri tipi di implementazione del servizio dichiarati come internal e sealed, questo codice dimostra che non tutti i servizi devono essere interfacce.
Aggiornare la Program classe
Aprire il file Program.cs e sostituire il codice esistente con il codice C# seguente:
using Microsoft.Extensions.DependencyInjection;
// 1. Create the service collection.
var services = new ServiceCollection();
// 2. Register (add and configure) the services.
services.AddSingleton<IConsole>(
implementationFactory: static _ => new DefaultConsole
{
IsEnabled = true
});
services.AddSingleton<IGreetingService, DefaultGreetingService>();
services.AddSingleton<FarewellService>();
// 3. Build the service provider from the service collection.
var serviceProvider = services.BuildServiceProvider();
// 4. Resolve the services that you need.
var greetingService = serviceProvider.GetRequiredService<IGreetingService>();
var farewellService = serviceProvider.GetRequiredService<FarewellService>();
// 5. Use the services
var greeting = greetingService.Greet("David");
var farewell = farewellService.SayGoodbye("David");
Il codice precedente illustra come:
- Creare una nuova
ServiceCollectionistanza. - Registrare e configurare i servizi in
ServiceCollection:- Il servizio
IConsoleutilizzando l'overload della factory di implementazione. Restituire un tipoDefaultConsolecon la proprietàIsEnabledimpostata sutrue. - Servizio
IGreetingServicecon un tipo di implementazione corrispondente diDefaultGreetingService. - Il servizio
FarewellServicecome tipo concreto.
- Il servizio
- Compilare il
ServiceProviderdalServiceCollection. - Risolvere i servizi
IGreetingServiceeFarewellService. - Usare i servizi risolti per salutare e congedarsi da una persona denominata
David.
Se si aggiorna la IsEnabled proprietà di DefaultConsole in false, i Greet metodi e SayGoodbye omettono di scrivere i messaggi risultanti nella console. Questa modifica consente di dimostrare che il IConsole servizio viene inserito nei servizi IGreetingService e FarewellService come una dipendenza che influenza il comportamento dell'app.
Tutti questi servizi vengono registrati come "singleton". Per questo esempio, funziona in modo identico se vengono registrati come servizi transienti o a durata definita.
Importante
In questo esempio forzato, le durate del servizio non sono importanti. In un'applicazione reale, considerare attentamente la durata di ogni servizio.
Eseguire l'app di esempio
Per eseguire l'app di esempio, premere F5 in Visual Studio o Visual Studio Code oppure eseguire il dotnet run comando nel terminale. Al termine dell'app, viene visualizzato l'output seguente:
Hello, David!
Goodbye, David!
Descrittori del servizio
Le API più comunemente usate per l'aggiunta di servizi a ServiceCollection sono metodi di estensione generici nominati per la durata della vita, ad esempio:
AddSingleton<TService>AddTransient<TService>AddScoped<TService>
Questi metodi sono metodi di convenienza che creano un'istanza di ServiceDescriptor e la aggiungono a ServiceCollection.
ServiceDescriptor è una classe semplice che descrive un servizio con il tipo di servizio, il tipo di implementazione e la durata. Può anche descrivere le factory di implementazione e le istanze.
Per ogni servizio registrato in ServiceCollection, è invece possibile chiamare direttamente il metodo Add con un'istanza di ServiceDescriptor. Si considerino gli esempi seguenti:
services.Add(ServiceDescriptor.Describe(
serviceType: typeof(IConsole),
implementationFactory: static _ => new DefaultConsole
{
IsEnabled = true
},
lifetime: ServiceLifetime.Singleton));
Il codice precedente equivale alla modalità di registrazione del IConsole servizio in ServiceCollection. Il Add metodo aggiunge un'istanza ServiceDescriptor che descrive il IConsole servizio. Il metodo statico ServiceDescriptor.Describe delega a vari costruttori ServiceDescriptor. Si consideri il codice equivalente per il IGreetingService servizio:
services.Add(ServiceDescriptor.Describe(
serviceType: typeof(IGreetingService),
implementationType: typeof(DefaultGreetingService),
lifetime: ServiceLifetime.Singleton));
Il codice precedente descrive il servizio IGreetingService con il tipo di servizio, il tipo di implementazione e il ciclo di vita. Infine, considerare il codice equivalente relativo al FarewellService servizio.
services.Add(ServiceDescriptor.Describe(
serviceType: typeof(FarewellService),
implementationType: typeof(FarewellService),
lifetime: ServiceLifetime.Singleton));
Il codice precedente descrive il tipo concreto FarewellService sia come tipo di servizio che di implementazione. Il servizio viene registrato come servizio singleton.