Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
In dit artikel maakt u een .NET-console-app om handmatig een ServiceCollection en bijbehorende ServiceProvider te maken. U leert hoe u services registreert en deze kunt oplossen met behulp van afhankelijkheidsinjectie (DI). In dit artikel wordt het NuGet-pakket Microsoft.Extensions.DependencyInjection gebruikt om de basisprincipes van DI in .NET te demonstreren.
Notitie
Dit artikel maakt geen gebruik van de algemene hostfuncties . Zie Afhankelijkheidsinjectie gebruiken in .NET voor een uitgebreidere handleiding.
Aan de slag
Maak eerst een nieuwe .NET-consoletoepassing met de naam DI.Basics. In de volgende lijst wordt verwezen naar een aantal van de meest voorkomende benaderingen voor het maken van een consoleproject:
- Visual Studio: menu Bestand > Nieuw > Project .
- Visual Studio Code en de C# Dev Kit-extensie menuoptie: Solution Explorer.
-
.NET CLI:
dotnet new console
opdracht in de terminal.
U moet de pakketverwijzing toevoegen aan Microsoft.Extensions.DependencyInjection in het projectbestand. Zorg er, ongeacht de benadering, voor dat het project lijkt op de volgende XML van het bestand DI.Basics.csproj :
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.7" />
</ItemGroup>
</Project>
Basisbeginselen van afhankelijkheidsinjectie
Afhankelijkheidsinjectie is een ontwerppatroon waarmee u in code vastgelegde afhankelijkheden kunt verwijderen en uw toepassing beter onderhoudbaar en testbaar kunt maken. DI is een techniek voor het bereiken van Inversion of Control (IoC) tussen klassen en hun afhankelijkheden.
De abstracties voor DI in .NET worden gedefinieerd in het NuGet-pakket Microsoft.Extensions.DependencyInjection.Abstractions :
- IServiceCollection: Definieert een contract voor een verzameling servicedescriptors.
- IServiceProvider: Definieert een mechanisme voor het ophalen van een serviceobject.
- ServiceDescriptor: Beschrijft een service met het servicetype, de implementatie en de levensduur.
In .NET wordt DI beheerd door services toe te voegen en te configureren in een IServiceCollection
. Nadat services zijn geregistreerd, wordt een IServiceProvider
exemplaar gebouwd door de BuildServiceProvider methode aan te roepen. De IServiceProvider
fungeert als een container van alle geregistreerde services en wordt gebruikt om services op te lossen.
Voorbeeldservices maken
Niet alle diensten worden gelijk gecreëerd. Voor sommige services is elke keer een nieuw exemplaar vereist wanneer de servicecontainer deze ophaalt (tijdelijke), terwijl andere tussen verschillende aanvragen moeten worden gedeeld (gescopeerd) of voor de hele levensduur van de app (singleton). Zie Servicelevensduur voor meer informatie over de levensduur van de service.
Op dezelfde manier maken sommige services alleen een concreet type beschikbaar, terwijl andere worden uitgedrukt als een contract tussen een interface en een implementatietype. U maakt verschillende variaties van services om deze concepten te demonstreren.
Maak een nieuw C#-bestand met de naam IConsole.cs en voeg de volgende code toe:
public interface IConsole
{
void WriteLine(string message);
}
Dit bestand definieert een IConsole
interface die één methode beschikbaar maakt. WriteLine
Maak vervolgens een nieuw C#-bestand met de naam DefaultConsole.cs en voeg de volgende code toe:
internal sealed class DefaultConsole : IConsole
{
public bool IsEnabled { get; set; } = true;
void IConsole.WriteLine(string message)
{
if (IsEnabled is false)
{
return;
}
Console.WriteLine(message);
}
}
De voorgaande code vertegenwoordigt de standaard implementatie van de IConsole
interface. De WriteLine
methode schrijft voorwaardelijk naar de console op basis van de IsEnabled
eigenschap.
Aanbeveling
De naamgeving van een implementatie is een keuze waar uw ontwikkelteam het mee eens moet zijn. Het Default
voorvoegsel is een algemene conventie om een standaard implementatie van een interface aan te geven, maar dit is niet vereist.
Maak vervolgens een IGreetingService.cs-bestand en voeg de volgende C#-code toe:
public interface IGreetingService
{
string Greet(string name);
}
Voeg vervolgens een nieuw C#-bestand toe met de naam DefaultGreetingService.cs en voeg de volgende code toe:
internal sealed class DefaultGreetingService(
IConsole console) : IGreetingService
{
public string Greet(string name)
{
var greeting = $"Hello, {name}!";
console.WriteLine(greeting);
return greeting;
}
}
De voorgaande code vertegenwoordigt de standaard implementatie van de IGreetingService
interface. Voor de service-implementatie is een IConsole
primaire constructorparameter vereist. De methode Greet
:
- Hiermee maakt u een
greeting
op basis vanname
. - Roept de
WriteLine
methode op hetIConsole
exemplaar aan. - Retourneert de
greeting
aan de oproeper.
De laatste service die moet worden gemaakt, is het FarewellService.cs-bestand . Voeg de volgende C#-code toe voordat u doorgaat:
public class FarewellService(IConsole console)
{
public string SayGoodbye(string name)
{
var farewell = $"Goodbye, {name}!";
console.WriteLine(farewell);
return farewell;
}
}
Het FarewellService
vertegenwoordigt een concreet type, geen interface. Het moet als public
worden aangegeven om het toegankelijk te maken voor consumenten. In tegenstelling tot andere soorten service-implementaties die als internal
en sealed
zijn gedeclareerd, laat deze code zien dat niet alle services interfaces hoeven te zijn. Het toont ook aan dat service-implementaties kunnen zijn sealed
om overname te voorkomen en internal
de toegang tot de assembly te beperken.
De Program
klasse bijwerken
Open het bestand Program.cs en vervang de bestaande code door de volgende C#-code:
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");
De voorgaande bijgewerkte code demonstreert de werkwijze:
- Maak een nieuwe
ServiceCollection
instantie. - Services registreren en configureren in het
ServiceCollection
:- De
IConsole
, gebruikmakend van de overload functie van de implementatiefactory, retourneert eenDefaultConsole
-type met deIsEnabled
ingesteld optrue
. - De
IGreetingService
wordt toegevoegd met een bijbehorend implementatietype vanDefaultGreetingService
type. - De
FarewellService
wordt toegevoegd als een concreet type.
- De
- Bouw de
ServiceProvider
van deServiceCollection
. - Los de
IGreetingService
- enFarewellService
-diensten op. - Gebruik de opgeloste services om een persoon met de naam
David
te begroeten en afscheid te nemen.
Als u de IsEnabled
eigenschap van de DefaultConsole
false
bijwerkt, wordt het schrijven van de resulterende berichten naar de console weggelaten door de Greet
en SayGoodbye
methoden. Een dergelijke wijziging helpt om aan te tonen dat de IConsole
service wordt geïnjecteerd in de IGreetingService
en FarewellService
services als een afhankelijkheid die van invloed is op het gedrag van apps.
Al deze services worden geregistreerd als singletons, maar voor dit voorbeeld werkt het identiek als ze zouden zijn geregistreerd als tijdelijke of gescopeerde-services.
Belangrijk
In dit voorbeeld maakt de levensduur van de service niet uit, maar in een echte toepassing moet u zorgvuldig rekening houden met de levensduur van elke service.
De voorbeeld-app uitvoeren
Als u de voorbeeld-app wilt uitvoeren, drukt u op F5 in Visual Studio, Visual Studio Code of voert u de dotnet run
opdracht uit in de terminal. Wanneer de app is voltooid, ziet u de volgende uitvoer:
Hello, David!
Goodbye, David!
Servicebeschrijvingen
De meest gebruikte API's voor het toevoegen van services aan de ServiceCollection
zijn generieke extensiemethoden met een specifieke levensduurbenaming, zoals:
AddSingleton<TService>
AddTransient<TService>
AddScoped<TService>
Deze methoden bieden handige mogelijkheden om een ServiceDescriptor exemplaar te maken en toe te voegen aan de ServiceCollection
. De ServiceDescriptor
is een eenvoudige klasse die een service beschrijft met het servicetype, het implementatietype en de levensduur. Het kan ook implementatiefabrieken en instanties beschrijven.
Voor elk van de services die u hebt geregistreerd in de ServiceCollection
, kunt u in plaats daarvan de Add
methode rechtstreeks aanroepen met een ServiceDescriptor
instantie. Bekijk de volgende voorbeelden:
services.Add(ServiceDescriptor.Describe(
serviceType: typeof(IConsole),
implementationFactory: static _ => new DefaultConsole
{
IsEnabled = true
},
lifetime: ServiceLifetime.Singleton));
De voorgaande code is gelijk aan de wijze waarop de IConsole
service is geregistreerd in de ServiceCollection
. De Add
methode wordt gebruikt om een ServiceDescriptor
exemplaar toe te voegen dat de IConsole
service beschrijft. De statische methode ServiceDescriptor.Describe
delegeert aan verschillende ServiceDescriptor
constructoren. Overweeg de equivalente code voor de IGreetingService
service:
services.Add(ServiceDescriptor.Describe(
serviceType: typeof(IGreetingService),
implementationType: typeof(DefaultGreetingService),
lifetime: ServiceLifetime.Singleton));
In de voorgaande code wordt de service beschreven met het servicetype, het implementatietype en de IGreetingService
levensduur. Overweeg ten slotte de equivalente code voor de FarewellService
service:
services.Add(ServiceDescriptor.Describe(
serviceType: typeof(FarewellService),
implementationType: typeof(FarewellService),
lifetime: ServiceLifetime.Singleton));
In de voorgaande code wordt het concrete FarewellService
type beschreven als zowel de service- als de implementatietypen. De service is geregistreerd als een singleton-service.