Injektáž závislostí .NET
.NET podporuje model návrhu softwaru injektáž závislostí (DI), což je technika pro dosažení inverze řízení (IoC) mezi třídami a jejich závislostmi. Injektáž závislostí v .NET je integrovaná součást architektury spolu s konfigurací, protokolováním a vzorem možností.
Závislost je objekt, na který závisí jiný objekt. Prozkoumejte následující MessageWriter
třídu pomocí Write
metody, na které závisí jiné třídy:
public class MessageWriter
{
public void Write(string message)
{
Console.WriteLine($"MessageWriter.Write(message: \"{message}\")");
}
}
Třída může vytvořit instanci MessageWriter
třídy, aby využívala její Write
metodu. V následujícím příkladu MessageWriter
je třída závislostí Worker
třídy:
public class Worker : BackgroundService
{
private readonly MessageWriter _messageWriter = new();
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_messageWriter.Write($"Worker running at: {DateTimeOffset.Now}");
await Task.Delay(1_000, stoppingToken);
}
}
}
Třída vytvoří a přímo závisí na MessageWriter
třídě. Pevně zakódované závislosti, například v předchozím příkladu, jsou problematické a měly by se vyhnout z následujících důvodů:
- Chcete-li nahradit
MessageWriter
jinou implementací,Worker
musí být třída změněna. - Pokud
MessageWriter
obsahuje závislosti, musí je také nakonfigurovatWorker
třída. Ve velkém projektu s více třídami v závislosti naMessageWriter
tom se konfigurační kód rozdělí v aplikaci. - Tato implementace je obtížná pro testování jednotek. Aplikace by měla používat napodobení nebo třídu zástupných procedur
MessageWriter
, která s tímto přístupem není možná.
Injektáž závislostí řeší tyto problémy prostřednictvím:
- Použití rozhraní nebo základní třídy k abstrakci implementace závislostí.
- Registrace závislosti v kontejneru služby .NET poskytuje integrovaný kontejner služby . IServiceProvider Služby se obvykle registrují v start-upu aplikace a připojí se k sadě IServiceCollection. Po přidání všech služeb vytvoříte BuildServiceProvider kontejner služby.
- Injektáž služby do konstruktoru třídy, ve které se používá. Architektura přebírá odpovědnost za vytvoření instance závislosti a její likvidaci, když už ji nepotřebujete.
IMessageWriter
Například rozhraní definuje metoduWrite
:
namespace DependencyInjection.Example;
public interface IMessageWriter
{
void Write(string message);
}
Toto rozhraní je implementováno konkrétním typem: MessageWriter
namespace DependencyInjection.Example;
public class MessageWriter : IMessageWriter
{
public void Write(string message)
{
Console.WriteLine($"MessageWriter.Write(message: \"{message}\")");
}
}
Vzorový kód zaregistruje IMessageWriter
službu s konkrétním typem MessageWriter
. Metoda AddSingleton zaregistruje službu s jednou životností, životností aplikace. Životnosti služeb jsou popsány dále v tomto článku.
using DependencyInjection.Example;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();
builder.Services.AddSingleton<IMessageWriter, MessageWriter>();
using IHost host = builder.Build();
host.Run();
V předchozím kódu ukázková aplikace:
Vytvoří instanci tvůrce hostitelských aplikací.
Nakonfiguruje služby registrací:
- Jako
Worker
hostovaná služba. Další informace naleznete v tématu Pracovní služby v .NET. - Rozhraní
IMessageWriter
jako jednoúčelová služba s odpovídající implementacíMessageWriter
třídy.
- Jako
Vytvoří hostitele a spustí ho.
Hostitel obsahuje poskytovatele služby injektáže závislostí. Obsahuje také všechny ostatní relevantní služby potřebné k automatickému vytvoření instance Worker
a poskytnutí odpovídající IMessageWriter
implementace jako argumentu.
namespace DependencyInjection.Example;
public sealed class Worker(IMessageWriter messageWriter) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
messageWriter.Write($"Worker running at: {DateTimeOffset.Now}");
await Task.Delay(1_000, stoppingToken);
}
}
}
Pomocí vzoru DI služba pracovního procesu:
- Nepoužívá konkrétní typ
MessageWriter
, pouzeIMessageWriter
rozhraní, které ho implementuje. To usnadňuje změnu implementace, kterou pracovní služba používá beze změny služby pracovního procesu. - Nevytvoří instanci
MessageWriter
. Instance je vytvořena kontejnerem DI.
Implementaci rozhraní je možné vylepšit pomocí integrovaného IMessageWriter
rozhraní API pro protokolování:
namespace DependencyInjection.Example;
public class LoggingMessageWriter(
ILogger<LoggingMessageWriter> logger) : IMessageWriter
{
public void Write(string message) =>
logger.LogInformation("Info: {Msg}", message);
}
Aktualizovaná AddSingleton
metoda zaregistruje novou IMessageWriter
implementaci:
builder.Services.AddSingleton<IMessageWriter, LoggingMessageWriter>();
Typ HostApplicationBuilder (builder
) je součástí Microsoft.Extensions.Hosting
balíčku NuGet.
LoggingMessageWriter
závisí na tom , který ILogger<TCategoryName>požaduje v konstruktoru. ILogger<TCategoryName>
je služba poskytovaná architekturou.
Není neobvyklé používat injektáž závislostí zřetězeným způsobem. Každá požadovaná závislost zase vyžaduje vlastní závislosti. Kontejner vyřeší závislosti v grafu a vrátí plně vyřešenou službu. Souhrnná sada závislostí, které je potřeba vyřešit, se obvykle označuje jako strom závislostí, graf závislostí nebo objektový graf.
Kontejner se ILogger<TCategoryName>
vyřeší tím, že využívá (obecné) otevřené typy a eliminuje potřebu registrovat každý (obecný) vytvořený typ.
Pomocí terminologie injektáže závislostí služba:
- Je obvykle objekt, který poskytuje službu jiným objektům, jako
IMessageWriter
je služba. - Nesouvisí s webovou službou, i když služba může používat webovou službu.
Architektura poskytuje robustní systém protokolování. Implementace IMessageWriter
uvedené v předchozích příkladech byly napsány tak, aby demonstrovaly základní DI, nikoli k implementaci protokolování. Většina aplikací by neměla zapisovat protokolovací nástroje. Následující kód ukazuje použití výchozího protokolování, které vyžaduje Worker
registraci pouze jako hostované služby AddHostedService:
public sealed class Worker(ILogger<Worker> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(1_000, stoppingToken);
}
}
}
Pomocí předchozího kódu není nutné aktualizovat Program.cs, protože rozhraní poskytuje protokolování.
Pravidla zjišťování více konstruktorů
Pokud typ definuje více než jeden konstruktor, poskytovatel služeb má logiku pro určení konstruktoru, který se má použít. Konstruktor s nejvíce parametry, ve kterých jsou typy di-přeložitelné, je vybrán. Představte si následující ukázkovou službu jazyka C#:
public class ExampleService
{
public ExampleService()
{
}
public ExampleService(ILogger<ExampleService> logger)
{
// omitted for brevity
}
public ExampleService(FooService fooService, BarService barService)
{
// omitted for brevity
}
}
V předchozím kódu předpokládejme, že bylo přidáno protokolování a je možné ho přeložit od poskytovatele služeb, ale FooService
BarService
typy nejsou. Konstruktor s parametrem ILogger<ExampleService>
se používá k překladu ExampleService
instance. I když existuje konstruktor, který definuje více parametrů, FooService
a BarService
typy nejsou přeložitelné.
Pokud při zjišťování konstruktorů existuje nejednoznačnost, vyvolá se výjimka. Představte si následující ukázkovou službu jazyka C#:
public class ExampleService
{
public ExampleService()
{
}
public ExampleService(ILogger<ExampleService> logger)
{
// omitted for brevity
}
public ExampleService(IOptions<ExampleOptions> options)
{
// omitted for brevity
}
}
Upozorňující
Kód ExampleService
s nejednoznačnými parametry typu DI by vyvolal výjimku. Nedělejte to – účelem je ukázat, co je míněno "nejednoznačnými di-překladovými typy".
V předchozím příkladu existují tři konstruktory. První konstruktor je bez parametrů a nevyžaduje žádné služby od poskytovatele služeb. Předpokládejme, že do kontejneru DI byly přidány protokolování i možnosti a že se jedná o služby, které lze přeložit. Když se kontejner DI pokusí přeložit ExampleService
typ, vyvolá výjimku, protože dva konstruktory jsou nejednoznačné.
Nejednoznačnosti se můžete vyhnout definováním konstruktoru, který místo toho přijímá oba typy překladu DI:
public class ExampleService
{
public ExampleService()
{
}
public ExampleService(
ILogger<ExampleService> logger,
IOptions<ExampleOptions> options)
{
// omitted for brevity
}
}
Registrace skupin služeb pomocí rozšiřujících metod
Rozšíření Microsoftu používají konvenci pro registraci skupiny souvisejících služeb. Konvence spočívá v použití jedné Add{GROUP_NAME}
rozšiřující metody k registraci všech služeb vyžadovaných funkcí architektury. Například AddOptions metoda rozšíření zaregistruje všechny služby potřebné pro použití možností.
Služby poskytované architekturou
Při použití některého z dostupných vzorů hostitele nebo tvůrce aplikací se použijí výchozí hodnoty a rozhraní zaregistruje služby. Představte si některé z nejoblíbenějších vzorů pro hostitele a tvůrce aplikací:
- Host.CreateDefaultBuilder()
- Host.CreateApplicationBuilder()
- WebHost.CreateDefaultBuilder()
- WebApplication.CreateBuilder()
- WebAssemblyHostBuilder.CreateDefault
- MauiApp.CreateBuilder
Po vytvoření tvůrce z některého z těchto rozhraní API IServiceCollection
má služby definované architekturou v závislosti na konfiguraci hostitele. Pro aplikace založené na šablonách .NET by architektura mohla zaregistrovat stovky služeb.
Následující tabulka uvádí malou ukázku těchto služeb registrovaných architekturou:
Typ služby | Životnost |
---|---|
Microsoft.Extensions.DependencyInjection.IServiceScopeFactory | Singleton |
IHostApplicationLifetime | Singleton |
Microsoft.Extensions.Logging.ILogger<TCategoryName> | Singleton |
Microsoft.Extensions.Logging.ILoggerFactory | Singleton |
Microsoft.Extensions.ObjectPool.ObjectPoolProvider | Singleton |
Microsoft.Extensions.Options.IConfigureOptions<TOptions> | Přechodná |
Microsoft.Extensions.Options.IOptions<TOptions> | Singleton |
System.Diagnostics.DiagnosticListener | Singleton |
System.Diagnostics.DiagnosticSource | Singleton |
Životnost služeb
Služby je možné zaregistrovat v jedné z následujících životností:
Následující části popisují jednotlivé předchozí životnosti. Zvolte odpovídající dobu života pro každou zaregistrovanou službu.
Přechodná
Přechodné služby životnosti se vytvářejí při každém vyžádání z kontejneru služby. Chcete-li zaregistrovat službu jako přechodná, zavolejte AddTransient.
Vaplikacích Vzhledem k tomu, že se služby řeší a vytvářejí pokaždé, když se tato doba životnosti účtují a konstruují na jednotlivé požadavky. Další informace najdete v tématu Pokyny pro injektáž závislostí: Pokyny pro IDisposable pro přechodné a sdílené instance.
Rozsahem
U webových aplikací doba života s vymezeným oborem značí, že se služby vytvoří jednou podle požadavku klienta (připojení). Zaregistrujte služby s vymezeným oborem pomocí AddScoped.
V aplikacích, které zpracovávají požadavky, se na konci požadavku zlikvidují omezené služby.
Při použití Entity Framework Core AddDbContext metoda rozšíření ve výchozím nastavení registruje DbContext
typy s vymezenou životností.
Poznámka:
Nevyřešujte službu s vymezeným oborem z jednohotonu a dávejte pozor, abyste to neprováděli nepřímo, například prostřednictvím přechodné služby. Při zpracování následných požadavků může dojít k nesprávnému stavu služby. Je v pořádku:
- Vyřešte službu singleton z omezené nebo přechodné služby.
- Vyřešte vymezenou službu z jiné služby s vymezeným oborem nebo přechodnou službou.
Ve výchozím nastavení vyvolá řešení služby z jiné služby s delší životností výjimku. Další informace najdete v tématu Ověření oboru.
Singleton
Služby s jednou životností se vytvářejí buď:
- Při prvním vyžádání.
- Vývojář při poskytování instance implementace přímo do kontejneru. Tento přístup je zřídka nutný.
Každý další požadavek implementace služby z kontejneru injektáže závislostí používá stejnou instanci. Pokud aplikace vyžaduje jednoúčelové chování, povolte kontejneru služby správu životnosti služby. Neimplementujte vzor návrhu singleton a poskytněte kód pro odstranění singletonu. Služby by nikdy neměly být uvolněny kódem, který službu přeložil z kontejneru. Pokud je typ nebo továrna registrována jako singleton, kontejner automaticky odstraní jednoton.
Zaregistrujte jednoúčelové služby pomocí AddSingleton. Jednoúčelové služby musí být bezpečné pro přístup z více vláken a často se používají v bezstavových službách.
V aplikacích, které zpracovávají požadavky, se vyřadí jednoúčelové služby, když ServiceProvider se vyřadí při vypnutí aplikace. Vzhledem k tomu, že se paměť nevyvolá, dokud se aplikace nevypíná, zvažte použití paměti s jednou službou.
Metody registrace služby
Architektura poskytuje metody rozšíření registrace služeb, které jsou užitečné v konkrétních scénářích:
metoda | Automatic (Automaticky) objekt likvidace |
Více implementace |
Předávání args |
---|---|---|---|
Add{LIFETIME}<{SERVICE}, {IMPLEMENTATION}>() Příklad: services.AddSingleton<IMyDep, MyDep>(); |
Ano | Ano | No |
Add{LIFETIME}<{SERVICE}>(sp => new {IMPLEMENTATION}) Příklady: services.AddSingleton<IMyDep>(sp => new MyDep()); services.AddSingleton<IMyDep>(sp => new MyDep(99)); |
Ano | Ano | Yes |
Add{LIFETIME}<{IMPLEMENTATION}>() Příklad: services.AddSingleton<MyDep>(); |
Yes | No | Ne |
AddSingleton<{SERVICE}>(new {IMPLEMENTATION}) Příklady: services.AddSingleton<IMyDep>(new MyDep()); services.AddSingleton<IMyDep>(new MyDep(99)); |
No | Ano | Yes |
AddSingleton(new {IMPLEMENTATION}) Příklady: services.AddSingleton(new MyDep()); services.AddSingleton(new MyDep(99)); |
No | No | Ano |
Další informace o odstranění typu naleznete v části Vyřazení služeb .
Registrace služby pouze s typem implementace je ekvivalentní registraci této služby se stejným typem implementace a služby. Představte si například následující kód:
services.AddSingleton<ExampleService>();
To je ekvivalentem registrace služby ve službě i implementaci stejných typů:
services.AddSingleton<ExampleService, ExampleService>();
Tato ekvivalence je důvod, proč není možné zaregistrovat více implementací služby pomocí metod, které nepoužívají explicitní typ služby. Tyto metody mohou zaregistrovat více instancí služby, ale všechny budou mít stejný typ implementace .
K registraci více instancí služby stejného typu služby je možné použít některou z výše uvedených metod registrace služby. V následujícím příkladu AddSingleton
se jako typ služby volá dvakrát IMessageWriter
. Druhé volání, které AddSingleton
přepíše předchozí, když je vyřešeno jako IMessageWriter
a přidá do předchozího volání při vyřešení více služeb prostřednictvím IEnumerable<IMessageWriter>
. Služby se zobrazují v pořadí, v jakém byly zaregistrovány při vyřešení prostřednictvím IEnumerable<{SERVICE}>
.
using ConsoleDI.IEnumerableExample;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddSingleton<IMessageWriter, ConsoleMessageWriter>();
builder.Services.AddSingleton<IMessageWriter, LoggingMessageWriter>();
builder.Services.AddSingleton<ExampleService>();
using IHost host = builder.Build();
_ = host.Services.GetService<ExampleService>();
await host.RunAsync();
Předchozí vzorový zdrojový kód registruje dvě implementace IMessageWriter
.
using System.Diagnostics;
namespace ConsoleDI.IEnumerableExample;
public sealed class ExampleService
{
public ExampleService(
IMessageWriter messageWriter,
IEnumerable<IMessageWriter> messageWriters)
{
Trace.Assert(messageWriter is LoggingMessageWriter);
var dependencyArray = messageWriters.ToArray();
Trace.Assert(dependencyArray[0] is ConsoleMessageWriter);
Trace.Assert(dependencyArray[1] is LoggingMessageWriter);
}
}
Definuje ExampleService
dva parametry konstruktoru, jeden IMessageWriter
a .IEnumerable<IMessageWriter>
Jediné IMessageWriter
je poslední registrace implementace, zatímco IEnumerable<IMessageWriter>
představuje všechny registrované implementace.
Architektura také poskytuje TryAdd{LIFETIME}
rozšiřující metody, které zaregistrují službu pouze v případě, že ještě není zaregistrovaná implementace.
V následujícím příkladu volání AddSingleton
registrů ConsoleMessageWriter
jako implementace pro IMessageWriter
. Volání TryAddSingleton
nemá žádný účinek, protože IMessageWriter
již má zaregistrovanou implementaci:
services.AddSingleton<IMessageWriter, ConsoleMessageWriter>();
services.TryAddSingleton<IMessageWriter, LoggingMessageWriter>();
Nemá TryAddSingleton
žádný vliv, protože už byl přidán a pokus se nezdaří. Tento výraz ExampleService
by potvrdil následující:
public class ExampleService
{
public ExampleService(
IMessageWriter messageWriter,
IEnumerable<IMessageWriter> messageWriters)
{
Trace.Assert(messageWriter is ConsoleMessageWriter);
Trace.Assert(messageWriters.Single() is ConsoleMessageWriter);
}
}
Další informace naleznete v tématu:
Metody TryAddEnumerable(ServiceDescriptor) registrují službu pouze v případě, že ještě neexistuje implementace stejného typu. Více služeb je vyřešeno prostřednictvím IEnumerable<{SERVICE}>
. Při registraci služeb přidejte instanci, pokud ještě nebyl přidán jeden ze stejných typů. Autoři knihoven se používají TryAddEnumerable
k tomu, aby se zabránilo registraci více kopií implementace v kontejneru.
V následujícím příkladu první volání TryAddEnumerable
registruje MessageWriter
jako implementace pro IMessageWriter1
. Druhý hovor se zaregistruje MessageWriter
pro IMessageWriter2
. Třetí volání nemá žádný účinek, protože IMessageWriter1
již má zaregistrovanou implementaci MessageWriter
:
public interface IMessageWriter1 { }
public interface IMessageWriter2 { }
public class MessageWriter : IMessageWriter1, IMessageWriter2
{
}
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMessageWriter1, MessageWriter>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMessageWriter2, MessageWriter>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMessageWriter1, MessageWriter>());
Registrace služby je obecně nezávislá na objednávce s výjimkou registrace více implementací stejného typu.
IServiceCollection
je kolekce ServiceDescriptor objektů. Následující příklad ukazuje, jak zaregistrovat službu vytvořením a přidáním ServiceDescriptor
:
string secretKey = Configuration["SecretKey"];
var descriptor = new ServiceDescriptor(
typeof(IMessageWriter),
_ => new DefaultMessageWriter(secretKey),
ServiceLifetime.Transient);
services.Add(descriptor);
Integrované Add{LIFETIME}
metody používají stejný přístup. Podívejte se například na zdrojový kód AddScoped.
Chování injektáže konstruktoru
Služby je možné vyřešit pomocí:
- IServiceProvider
- ActivatorUtilities:
- Vytvoří objekty, které nejsou zaregistrované v kontejneru.
- Používá se s některými funkcemi architektury.
Konstruktory mohou přijímat argumenty, které nejsou poskytovány injektáží závislostí, ale argumenty musí přiřadit výchozí hodnoty.
Při řešení IServiceProvider
služeb pomocí injektáže konstruktoru nebo ActivatorUtilities
konstruktoru vyžaduje veřejný konstruktor.
Při řešení ActivatorUtilities
služeb pomocí injektáže konstruktoru vyžaduje, aby existoval pouze jeden použitelný konstruktor. Přetížení konstruktoru jsou podporována, ale pouze jedno přetížení může existovat, jehož argumenty mohou být splněny injektáž závislostí.
Ověření oboru
Když se aplikace spustí v Development
prostředí a zavolá createApplicationBuilder k sestavení hostitele, výchozí poskytovatel služeb provede kontroly, aby ověřil, že:
- Služby s vymezeným oborem se od poskytovatele kořenových služeb nepřeloží.
- Služby s vymezeným oborem se nevkážou do singletonů.
Po zavolání se vytvoří BuildServiceProvider poskytovatel kořenové služby. Životnost poskytovatele kořenových služeb odpovídá životnosti aplikace, když poskytovatel začne s aplikací a je uvolněn při vypnutí aplikace.
Omezené služby jsou uvolněny kontejnerem, který je vytvořil. Pokud se v kořenovém kontejneru vytvoří vymezená služba, životnost služby se efektivně zvýší na singleton, protože je odstraněna pouze kořenovým kontejnerem, když se aplikace vypne. Ověřování rozsahů služby tyto situace zachytí, když BuildServiceProvider
je volána.
Scénáře oborů
Je IServiceScopeFactory vždy registrován jako singleton, ale IServiceProvider může se lišit v závislosti na životnosti obsahující třídy. Pokud například přeložíte služby z oboru a každá z těchto služeb převezme IServiceProvidernějakou instanci, bude to instance s vymezeným oborem.
Pro dosažení rozsahu služeb v rámci implementací IHostedService, jako BackgroundServiceje například , nevkládat závislosti služby prostřednictvím injektáže konstruktoru. Místo toho vložte IServiceScopeFactory, vytvořte obor a pak přeložte závislosti z oboru tak, aby používala odpovídající dobu životnosti služby.
namespace WorkerScope.Example;
public sealed class Worker(
ILogger<Worker> logger,
IServiceScopeFactory serviceScopeFactory)
: BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
using (IServiceScope scope = serviceScopeFactory.CreateScope())
{
try
{
logger.LogInformation(
"Starting scoped work, provider hash: {hash}.",
scope.ServiceProvider.GetHashCode());
var store = scope.ServiceProvider.GetRequiredService<IObjectStore>();
var next = await store.GetNextAsync();
logger.LogInformation("{next}", next);
var processor = scope.ServiceProvider.GetRequiredService<IObjectProcessor>();
await processor.ProcessAsync(next);
logger.LogInformation("Processing {name}.", next.Name);
var relay = scope.ServiceProvider.GetRequiredService<IObjectRelay>();
await relay.RelayAsync(next);
logger.LogInformation("Processed results have been relayed.");
var marked = await store.MarkAsync(next);
logger.LogInformation("Marked as processed: {next}", marked);
}
finally
{
logger.LogInformation(
"Finished scoped work, provider hash: {hash}.{nl}",
scope.ServiceProvider.GetHashCode(), Environment.NewLine);
}
}
}
}
}
V předchozím kódu, zatímco je aplikace spuštěná, služba na pozadí:
- Závisí na závislosti IServiceScopeFactoryna .
- Vytvoří pro IServiceScope překlad dalších služeb.
- Řeší služby s vymezeným oborem pro spotřebu.
- Pracuje na zpracování objektů a jejich následné předávání a nakonec je označí jako zpracované.
Z ukázkového zdrojového kódu si můžete prohlédnout, jak můžou implementace těžit z rozsahu IHostedService životnosti služby.
Služby s klíči
Od .NET 8 existuje podpora registrací služeb a vyhledávání na základě klíče, což znamená, že je možné zaregistrovat více služeb s jiným klíčem a tento klíč použít pro vyhledávání.
Představte si například případ, kdy máte různé implementace rozhraní IMessageWriter
: MemoryMessageWriter
a QueueMessageWriter
.
Tyto služby můžete zaregistrovat pomocí přetížení metod registrace služby (vidět dříve), které podporují klíč jako parametr:
services.AddKeyedSingleton<IMessageWriter, MemoryMessageWriter>("memory");
services.AddKeyedSingleton<IMessageWriter, QueueMessageWriter>("queue");
key
Není omezen na string
, může být libovolnýobject
, pokud typ správně implementuje Equals
.
V konstruktoru třídy, která používá IMessageWriter
, přidáte FromKeyedServicesAttribute k zadání klíče služby k vyřešení:
public class ExampleService
{
public ExampleService(
[FromKeyedServices("queue")] IMessageWriter writer)
{
// Omitted for brevity...
}
}
Viz také
- Vysvětlení základů injektáže závislostí v .NET
- Použití injektáže závislostí v .NET
- Pokyny pro injektáž závislostí
- Injektáž závislostí v ASP.NET Core
- Vzory konferencí NDC pro vývoj aplikací DI
- Princip explicitních závislostí
- Inversion of control containers and the dependency injection pattern (Martin Fowler)
- V úložišti github.com/dotnet/extensions by se měly vytvořit chyby DI.