Vysvětlení základů injektáže závislostí v .NET
V tomto článku vytvoříte konzolovou aplikaci .NET, která ručně vytvoří ServiceCollection a odpovídá ServiceProvider. Naučíte se registrovat služby a řešit je pomocí injektáže závislostí (DI). Tento článek používá balíček NuGet Microsoft.Extensions.DependencyInjection k předvedení základů DI v .NET.
Poznámka:
Tento článek nevyužívá funkce obecného hostitele . Podrobnější průvodce najdete v tématu Použití injektáže závislostí v .NET.
Začínáme
Začněte vytvořením nové konzolové aplikace .NET s názvem DI.Basics. Na některé z nejběžnějších přístupů k vytvoření projektu konzoly se odkazuje v následujícím seznamu:
- Visual Studio: Nabídka Soubor > nový > projekt
- Visual Studio Code a rozšíření C# Dev Kit: možnost nabídky Průzkumník řešení.
- .NET CLI:
dotnet new console
příkaz v terminálu.
Do souboru projektu je potřeba přidat odkaz na balíček Microsoft.Extensions.DependencyInjection . Bez ohledu na přístup se ujistěte, že se projekt podobá následujícímu kódu XML souboru 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="8.0.0" />
</ItemGroup>
</Project>
Základy injektáže závislostí
Injektáž závislostí je vzor návrhu, který umožňuje odebrat pevně zakódované závislosti a zajistit, aby byla vaše aplikace lépe udržovatelná a testovatelná. DI je technika pro dosažení inverze řízení (IoC) mezi třídami a jejich závislostmi.
Abstrakce di di v .NET jsou definovány v balíčku NuGet Microsoft.Extensions.DependencyInjection.Abstractions :
- IServiceCollection: Definuje kontrakt pro kolekci popisovačů služby.
- IServiceProvider: Definuje mechanismus pro načtení objektu služby.
- ServiceDescriptor: Popisuje službu s jejím typem služby, implementací a životností.
V .NET se di spravuje přidáním služeb a jejich konfigurací v objektu IServiceCollection
. Po registraci IServiceProvider
služeb je instance vytvořena voláním BuildServiceProvider metody. Funguje IServiceProvider
jako kontejner všech registrovaných služeb a slouží k řešení služeb.
Vytváření example služeb
Ne všechny služby se vytvářejí stejně. Některé služby vyžadují novou instanci pokaždé, když je kontejner služby získá (transient), zatímco jiné by se měly sdílet mezi požadavky (scoped) nebo po celou dobu životnosti aplikace (singleton). Další informace o životnostech služeb najdete v tématu Životnost služby.
Podobně některé služby zpřístupňují pouze konkrétní typ, zatímco jiné jsou vyjádřeny jako kontrakt mezi rozhraním a typem implementace. Vytvoříte několik variant služeb, které vám pomůžou tyto koncepty předvést.
Vytvořte nový soubor C# s názvem IConsole.cs a přidejte následující kód:
public interface IConsole
{
void WriteLine(string message);
}
Tento soubor definuje IConsole
rozhraní, které zveřejňuje jednu metodu , WriteLine
. Dále vytvořte nový soubor C# s názvem DefaultConsole.cs a přidejte následující kód:
internal sealed class DefaultConsole : IConsole
{
public bool IsEnabled { get; set; } = true;
void IConsole.WriteLine(string message)
{
if (IsEnabled is false)
{
return;
}
Console.WriteLine(message);
}
}
Předchozí kód představuje výchozí implementaci IConsole
rozhraní. Metoda WriteLine
podmíněně zapisuje do konzoly na IsEnabled
základě vlastnosti.
Tip
Pojmenování implementace je volba, na které by se měl váš vývojový tým shodnout. Předpona Default
je běžnou konvencí označující výchozí implementaci rozhraní, ale nevyžaduje se.
Dále vytvořte soubor IGreetingService.cs a přidejte následující kód jazyka C#:
public interface IGreetingService
{
string Greet(string name);
}
Pak přidejte nový soubor C# s názvem DefaultGreetingService.cs a přidejte následující kód:
internal sealed class DefaultGreetingService(
IConsole console) : IGreetingService
{
public string Greet(string name)
{
var greeting = $"Hello, {name}!";
console.WriteLine(greeting);
return greeting;
}
}
Předchozí kód představuje výchozí implementaci IGreetingService
rozhraní. Implementace služby vyžaduje IConsole
jako primární parametr konstruktoru. Metoda Greet
:
- Vytvoří danou
greeting
.name
- Volá metodu
WriteLine
vIConsole
instanci. greeting
Vrátí volajícímu.
Poslední službou , která se má vytvořit, je soubor FarewellService.cs a před pokračováním přidejte následující kód jazyka C#:
public class FarewellService(IConsole console)
{
public string SayGoodbye(string name)
{
var farewell = $"Goodbye, {name}!";
console.WriteLine(farewell);
return farewell;
}
}
Představuje FarewellService
konkrétní typ, nikoli rozhraní. Měla by být deklarována tak public
, aby byla přístupná uživatelům. Na rozdíl od jiných typů implementace služby, které byly deklarovány jako internal
a sealed
, tento kód ukazuje, že ne všechny služby musí být rozhraní. Ukazuje také, že implementace služeb mohou bránit sealed
dědičnosti a internal
omezit přístup k sestavení.
Program
Aktualizace třídy
Otevřete soubor Program.cs a nahraďte stávající kód následujícím kódem jazyka C#:
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");
Předchozí aktualizovaný kód ukazuje postupy:
- Vytvořte novou
ServiceCollection
instanci. - Registrace a konfigurace služeb v :
ServiceCollection
- Použití
IConsole
přetížení implementační továrny vrátíDefaultConsole
typ s nastavenouIsEnabled
na hodnotu true. - Přidá se
IGreetingService
s odpovídajícím typemDefaultGreetingService
implementace. - Je
FarewellService
přidán jako betonový typ.
- Použití
- Sestavte z objektu
ServiceProvider
ServiceCollection
. - Řešení problémů
IGreetingService
aFarewellService
služeb - Pomocí vyřešených služeb pozdravte a rozloučíte se s osobou s názvem
David
.
Pokud aktualizujete IsEnabled
vlastnost DefaultConsole
to false
, Greet
a SayGoodbye
metody vynechat zápis do výsledných zpráv do konzoly. Taková změna vám pomůže předvést, že IConsole
se služba vloží do IGreetingService
FarewellService
služeb jako závislost , která ovlivňuje chování aplikací.
Všechny tyto služby jsou registrovány jako singletony, i když pro tuto ukázku, funguje stejně, pokud byly zaregistrovány jako transient nebo scoped služby.
Důležité
V této contrived example, životnosti služby nezáleží, ale v reálné aplikaci byste měli pečlivě zvážit životnost každé služby.
Spuštění ukázkové aplikace
Ukázkovou aplikaci spustíte stisknutím klávesy F5 v sadě Visual Studio, editoru Visual Studio Code nebo spuštěním dotnet run
příkazu v terminálu. Po dokončení aplikace by se měl zobrazit následující výstup:
Hello, David!
Goodbye, David!
Popisovače služeb
Nejčastěji používaná rozhraní API pro přidávání služeb do obecných ServiceCollection
rozšiřujících metod, jako jsou:
AddSingleton<TService>
AddTransient<TService>
AddScoped<TService>
Tyto metody jsou pohodlné metody, které vytvářejí ServiceDescriptor instanci a přidávají ji do .ServiceCollection
Jedná se ServiceDescriptor
o jednoduchou třídu, která popisuje službu s jejím typem služby, typem implementace a životností. Může také popisovat implementační továrny a instance.
Pro každou službu, kterou jste zaregistrovali v nástroji ServiceCollection
, můžete místo toho volat Add
metodu ServiceDescriptor
s instancí přímo. Zvažte následující příklady:
services.Add(ServiceDescriptor.Describe(
serviceType: typeof(IConsole),
implementationFactory: static _ => new DefaultConsole
{
IsEnabled = true
},
lifetime: ServiceLifetime.Singleton));
Předchozí kód je ekvivalentní tomu, jak IConsole
byla služba zaregistrována v ServiceCollection
souboru . Metoda Add
se používá k přidání ServiceDescriptor
instance, která popisuje IConsole
službu. Statická metoda ServiceDescriptor.Describe
deleguje na různé ServiceDescriptor
konstruktory. Vezměte v úvahu ekvivalentní kód pro IGreetingService
službu:
services.Add(ServiceDescriptor.Describe(
serviceType: typeof(IGreetingService),
implementationType: typeof(DefaultGreetingService),
lifetime: ServiceLifetime.Singleton));
Předchozí kód popisuje IGreetingService
službu s jejím typem služby, typem implementace a životností. Nakonec zvažte ekvivalentní kód pro FarewellService
službu:
services.Add(ServiceDescriptor.Describe(
serviceType: typeof(FarewellService),
implementationType: typeof(FarewellService),
lifetime: ServiceLifetime.Singleton));
Předchozí kód popisuje konkrétní FarewellService
typ jako typ služby i implementace. Služba je zaregistrovaná singleton jako služba.