.NET-beroendeinmatning
.NET stöder designmönstret för beroendeinmatning (DI), vilket är en teknik för att uppnå inversion av kontroll (IoC) mellan klasser och deras beroenden. Beroendeinmatning i .NET är en inbyggd del av ramverket, tillsammans med konfiguration, loggning och alternativmönstret.
Ett beroende är ett objekt som ett annat objekt är beroende av. Granska följande MessageWriter
klass med en Write
metod som andra klasser är beroende av:
public class MessageWriter
{
public void Write(string message)
{
Console.WriteLine($"MessageWriter.Write(message: \"{message}\")");
}
}
En klass kan skapa en instans av MessageWriter
klassen för att använda sin Write
metod. I följande exempel MessageWriter
är klassen ett beroende av Worker
klassen:
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);
}
}
}
Klassen skapar och är direkt beroende av MessageWriter
klassen. Hårdkodade beroenden, till exempel i föregående exempel, är problematiska och bör undvikas av följande skäl:
- Om du vill ersätta
MessageWriter
med en annan implementeringWorker
måste klassen ändras. - Om
MessageWriter
det finns beroenden måste de också konfigureras avWorker
klassen. I ett stort projekt med flera klasser beroende påMessageWriter
blir konfigurationskoden utspridd över appen. - Den här implementeringen är svår att enhetstesta. Appen bör använda en modell- eller stub-klass
MessageWriter
, vilket inte är möjligt med den här metoden.
Beroendeinmatning åtgärdar dessa problem genom:
- Användning av ett gränssnitt eller en basklass för att abstrahera beroendeimplementeringen.
- Registrering av beroendet i en tjänstcontainer. .NET tillhandahåller en inbyggd tjänstcontainer, IServiceProvider. Tjänsterna registreras vanligtvis vid appens start och läggs till i en IServiceCollection. När alla tjänster har lagts till använder BuildServiceProvider du för att skapa tjänstcontainern.
- Inmatning av tjänsten i konstruktorn för den klass där den används. Ramverket tar på sig ansvaret att skapa en instans av beroendet och ta bort det när det inte längre behövs.
Gränssnittet definierar Write
till exempel IMessageWriter
metoden:
namespace DependencyInjection.Example;
public interface IMessageWriter
{
void Write(string message);
}
Det här gränssnittet implementeras av en konkret typ, MessageWriter
:
namespace DependencyInjection.Example;
public class MessageWriter : IMessageWriter
{
public void Write(string message)
{
Console.WriteLine($"MessageWriter.Write(message: \"{message}\")");
}
}
Exempelkoden registrerar IMessageWriter
tjänsten med betongtypen MessageWriter
. Metoden AddSingleton registrerar tjänsten med en singleton-livslängd, appens livslängd. Tjänstens livslängd beskrivs senare i den här artikeln.
using DependencyInjection.Example;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();
builder.Services.AddSingleton<IMessageWriter, MessageWriter>();
using IHost host = builder.Build();
host.Run();
I föregående kod, exempelappen:
Skapar en värdappsbyggareinstans.
Konfigurerar tjänsterna genom att registrera:
- Som
Worker
en värdbaserad tjänst. Mer information finns i Worker Services i .NET. - Gränssnittet
IMessageWriter
som en singleton-tjänst med en motsvarande implementering avMessageWriter
klassen.
- Som
Skapar värden och kör den.
Värden innehåller providern för beroendeinmatningstjänsten. Den innehåller också alla andra relevanta tjänster som krävs för att automatiskt instansiera Worker
och tillhandahålla motsvarande IMessageWriter
implementering som ett argument.
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);
}
}
}
Med hjälp av DI-mönstret:
- Använder inte betongtypen
MessageWriter
, bara gränssnittetIMessageWriter
som implementerar den. Det gör det enkelt att ändra den implementering som arbetartjänsten använder utan att ändra arbetstjänsten. - Skapar inte en instans av
MessageWriter
. Instansen skapas av DI-containern.
Implementeringen av IMessageWriter
gränssnittet kan förbättras med hjälp av det inbyggda loggnings-API:et:
namespace DependencyInjection.Example;
public class LoggingMessageWriter(
ILogger<LoggingMessageWriter> logger) : IMessageWriter
{
public void Write(string message) =>
logger.LogInformation("Info: {Msg}", message);
}
Den uppdaterade AddSingleton
metoden registrerar den nya IMessageWriter
implementeringen:
builder.Services.AddSingleton<IMessageWriter, LoggingMessageWriter>();
Typen HostApplicationBuilder (builder
) är en del av NuGet-paketet Microsoft.Extensions.Hosting
.
LoggingMessageWriter
beror på ILogger<TCategoryName>, som den begär i konstruktorn. ILogger<TCategoryName>
är en ramverksbaserad tjänst.
Det är inte ovanligt att använda beroendeinmatning på ett kedjat sätt. Varje begärt beroende begär i sin tur sina egna beroenden. Containern löser beroendena i diagrammet och returnerar den fullständigt lösta tjänsten. Den kollektiva uppsättningen beroenden som måste lösas kallas vanligtvis ett beroendeträd, beroendediagram eller objektdiagram.
Containern löser ILogger<TCategoryName>
problemet genom att dra nytta av (generiska) öppna typer, vilket eliminerar behovet av att registrera alla (generiska) konstruerade typer.
Med terminologi för beroendeinmatning, en tjänst:
- Är vanligtvis ett objekt som tillhandahåller en tjänst till andra objekt, till exempel tjänsten
IMessageWriter
. - Är inte relaterad till en webbtjänst, även om tjänsten kan använda en webbtjänst.
Ramverket tillhandahåller ett robust loggningssystem. Implementeringarna IMessageWriter
som visas i föregående exempel skrevs för att demonstrera grundläggande DI, inte för att implementera loggning. De flesta appar ska inte behöva skriva loggare. Följande kod visar hur du använder standardloggningen, som endast kräver att den Worker
registreras som en värdbaserad tjänst 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);
}
}
}
Med hjälp av föregående kod behöver du inte uppdatera Program.cs eftersom loggning tillhandahålls av ramverket.
Flera identifieringsregler för konstruktor
När en typ definierar mer än en konstruktor har tjänstleverantören logik för att avgöra vilken konstruktor som ska användas. Konstruktorn med de flesta parametrar där typerna är DI-matchbara väljs. Överväg följande C#-exempeltjänst:
public class ExampleService
{
public ExampleService()
{
}
public ExampleService(ILogger<ExampleService> logger)
{
// omitted for brevity
}
public ExampleService(FooService fooService, BarService barService)
{
// omitted for brevity
}
}
I föregående kod förutsätter du att loggning har lagts till och kan matchas från tjänstleverantören, men det är inte typerna FooService
och BarService
. Konstruktorn med parametern ILogger<ExampleService>
används för att lösa instansen ExampleService
. Även om det finns en konstruktor som definierar fler parametrar är typerna FooService
och BarService
inte DI-matchbara.
Om det finns tvetydigheter vid identifiering av konstruktorer utlöses ett undantag. Överväg följande C#-exempeltjänst:
public class ExampleService
{
public ExampleService()
{
}
public ExampleService(ILogger<ExampleService> logger)
{
// omitted for brevity
}
public ExampleService(IOptions<ExampleOptions> options)
{
// omitted for brevity
}
}
Varning
Koden ExampleService
med tvetydiga DI-matchningsbara typparametrar skulle utlösa ett undantag. Gör inte detta – det är avsett att visa vad som menas med "tvetydiga DI-matchningsbara typer".
I föregående exempel finns det tre konstruktorer. Den första konstruktorn är parameterlös och kräver inga tjänster från tjänstleverantören. Anta att både loggning och alternativ har lagts till i DI-containern och är DI-matchningsbara tjänster. När DI-containern försöker matcha ExampleService
typen utlöser den ett undantag eftersom de två konstruktorerna är tvetydiga.
Du kan undvika tvetydighet genom att definiera en konstruktor som accepterar båda DI-matchningsbara typerna i stället:
public class ExampleService
{
public ExampleService()
{
}
public ExampleService(
ILogger<ExampleService> logger,
IOptions<ExampleOptions> options)
{
// omitted for brevity
}
}
Registrera grupper av tjänster med tilläggsmetoder
Microsoft Extensions använder en konvention för att registrera en grupp relaterade tjänster. Konventionen är att använda en enda Add{GROUP_NAME}
tilläggsmetod för att registrera alla tjänster som krävs av en ramverksfunktion. Till exempel AddOptions registrerar tilläggsmetoden alla tjänster som krävs för att använda alternativ.
Ramverksbaserade tjänster
När du använder något av de tillgängliga värd- eller appbyggarmönstren tillämpas standardvärden och tjänster registreras av ramverket. Överväg några av de mest populära värd- och appbyggarmönstren:
- Host.CreateDefaultBuilder()
- Host.CreateApplicationBuilder()
- WebHost.CreateDefaultBuilder()
- WebApplication.CreateBuilder()
- WebAssemblyHostBuilder.CreateDefault
- MauiApp.CreateBuilder
När du har skapat en byggare från någon av dessa API:er IServiceCollection
har tjänsterna definierats av ramverket, beroende på hur värden har konfigurerats. För appar baserade på .NET-mallar kan ramverket registrera hundratals tjänster.
I följande tabell visas ett litet urval av dessa ramverksregistrerade tjänster:
Tjänsttyp | Livstid |
---|---|
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> | Tillfälligt |
Microsoft.Extensions.Options.IOptions<TOptions> | Singleton |
System.Diagnostics.DiagnosticListener | Singleton |
System.Diagnostics.DiagnosticSource | Singleton |
Tjänstlivslängd
Tjänster kan registreras med någon av följande livslängder:
I följande avsnitt beskrivs var och en av de föregående livslängderna. Välj en lämplig livslängd för varje registrerad tjänst.
Tillfälligt
Tillfälliga livslängdstjänster skapas varje gång de begärs från tjänstcontainern. Om du vill registrera en tjänst som tillfällig anropar du AddTransient.
I appar som bearbetar begäranden tas tillfälliga tjänster bort i slutet av begäran. Den här livslängden medför allokeringar per/begäran, eftersom tjänster löses och konstrueras varje gång. Mer information finns i Riktlinjer för beroendeinmatning: IDisposable-vägledning för tillfälliga och delade instanser.
Scoped
För webbprogram anger en begränsad livslängd att tjänster skapas en gång per klientbegäran (anslutning). Registrera begränsade tjänster med AddScoped.
I appar som bearbetar begäranden tas begränsade tjänster bort i slutet av begäran.
När du använder Entity Framework Core AddDbContext registrerar DbContext
tilläggsmetoden typer med en begränsad livslängd som standard.
Kommentar
Lös inte en begränsad tjänst från en singleton och var noga med att inte göra det indirekt, till exempel via en tillfällig tjänst. Det kan orsaka att tjänsten har felaktigt tillstånd när efterföljande begäranden bearbetas. Det är okej att:
- Lös en singleton-tjänst från en begränsad eller tillfällig tjänst.
- Lös en begränsad tjänst från en annan begränsad eller tillfällig tjänst.
I utvecklingsmiljön genererar som standard en lösning av en tjänst från en annan tjänst med längre livslängd ett undantag. Mer information finns i Omfångsverifiering.
Singleton
Singleton Lifetime-tjänster skapas antingen:
- Första gången de begärs.
- Av utvecklaren när du tillhandahåller en implementeringsinstans direkt till containern. Den här metoden behövs sällan.
Varje efterföljande begäran om tjänstimplementeringen från containern för beroendeinmatning använder samma instans. Om appen kräver singleton-beteende tillåter du att tjänstcontainern hanterar tjänstens livslängd. Implementera inte designmönstret för singleton och ange kod för att ta bort singletonen. Tjänster ska aldrig tas bort med kod som löste tjänsten från containern. Om en typ eller fabrik registreras som en singleton tas singletonen bort automatiskt av containern.
Registrera singleton-tjänster med AddSingleton. Singleton-tjänster måste vara trådsäkra och används ofta i tillståndslösa tjänster.
I appar som bearbetar begäranden tas singleton-tjänster bort när de ServiceProvider tas bort vid programavstängning. Eftersom minnet inte släpps förrän appen har stängts av bör du överväga minnesanvändning med en singleton-tjänst.
Metoder för tjänstregistrering
Ramverket tillhandahåller metoder för tjänstregistreringstillägg som är användbara i specifika scenarier:
Metod | Automatisk objekt förfogande |
Flera Implementeringar |
Passera args |
---|---|---|---|
Add{LIFETIME}<{SERVICE}, {IMPLEMENTATION}>() Exempel: services.AddSingleton<IMyDep, MyDep>(); |
Ja | Ja | Nej |
Add{LIFETIME}<{SERVICE}>(sp => new {IMPLEMENTATION}) Exempel: services.AddSingleton<IMyDep>(sp => new MyDep()); services.AddSingleton<IMyDep>(sp => new MyDep(99)); |
Ja | Ja | Ja |
Add{LIFETIME}<{IMPLEMENTATION}>() Exempel: services.AddSingleton<MyDep>(); |
Ja | Nej | Nej |
AddSingleton<{SERVICE}>(new {IMPLEMENTATION}) Exempel: services.AddSingleton<IMyDep>(new MyDep()); services.AddSingleton<IMyDep>(new MyDep(99)); |
Nej | Ja | Ja |
AddSingleton(new {IMPLEMENTATION}) Exempel: services.AddSingleton(new MyDep()); services.AddSingleton(new MyDep(99)); |
Nej | Nej | Ja |
Mer information om typhantering finns i avsnittet Avyttring av tjänster .
Registrering av en tjänst med endast en implementeringstyp motsvarar registrering av tjänsten med samma implementerings- och tjänsttyp. Tänk till exempel på följande kod:
services.AddSingleton<ExampleService>();
Detta motsvarar registrering av tjänsten med både tjänsten och implementeringen av samma typer:
services.AddSingleton<ExampleService, ExampleService>();
Den här likvärdigheten är anledningen till att flera implementeringar av en tjänst inte kan registreras med de metoder som inte använder en explicit tjänsttyp. Dessa metoder kan registrera flera instanser av en tjänst, men alla har samma implementeringstyp .
Någon av ovanstående tjänstregistreringsmetoder kan användas för att registrera flera tjänstinstanser av samma tjänsttyp. I följande exempel AddSingleton
anropas två gånger med IMessageWriter
som tjänsttyp. Det andra anropet till AddSingleton
åsidosätter det föregående när det matchas som IMessageWriter
och läggs till i föregående när flera tjänster matchas via IEnumerable<IMessageWriter>
. Tjänsterna visas i den ordning de registrerades när de löstes via 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();
Föregående exempelkälla registrerar två implementeringar av 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);
}
}
Definierar ExampleService
två konstruktorparametrar: en enda IMessageWriter
, och en IEnumerable<IMessageWriter>
. Den enda IMessageWriter
är den sista implementeringen som har registrerats, medan den IEnumerable<IMessageWriter>
representerar alla registrerade implementeringar.
Ramverket innehåller TryAdd{LIFETIME}
också tilläggsmetoder som endast registrerar tjänsten om det inte redan finns en registrerad implementering.
I följande exempel registreras anropet till AddSingleton
som en implementering för IMessageWriter
.ConsoleMessageWriter
Anropet till TryAddSingleton
har ingen effekt eftersom IMessageWriter
det redan har en registrerad implementering:
services.AddSingleton<IMessageWriter, ConsoleMessageWriter>();
services.TryAddSingleton<IMessageWriter, LoggingMessageWriter>();
Har TryAddSingleton
ingen effekt, eftersom det redan har lagts till och "try" misslyckas. Skulle ExampleService
hävda följande:
public class ExampleService
{
public ExampleService(
IMessageWriter messageWriter,
IEnumerable<IMessageWriter> messageWriters)
{
Trace.Assert(messageWriter is ConsoleMessageWriter);
Trace.Assert(messageWriters.Single() is ConsoleMessageWriter);
}
}
Mer information finns i:
Metoderna TryAddEnumerable (ServiceDescriptor) registrerar endast tjänsten om det inte redan finns en implementering av samma typ. Flera tjänster löses via IEnumerable<{SERVICE}>
. När du registrerar tjänster lägger du till en instans om en av samma typer inte redan har lagts till. Biblioteksförfattare använder TryAddEnumerable
för att undvika att registrera flera kopior av en implementering i containern.
I följande exempel registreras MessageWriter
det första anropet till TryAddEnumerable
som en implementering för IMessageWriter1
. Det andra anropet registreras MessageWriter
för IMessageWriter2
. Det tredje anropet har ingen effekt eftersom IMessageWriter1
det redan har en registrerad implementering av 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>());
Tjänstregistrering är vanligtvis orderoberoende, förutom när du registrerar flera implementeringar av samma typ.
IServiceCollection
är en samling ServiceDescriptor objekt. I följande exempel visas hur du registrerar en tjänst genom att skapa och lägga till en ServiceDescriptor
:
string secretKey = Configuration["SecretKey"];
var descriptor = new ServiceDescriptor(
typeof(IMessageWriter),
_ => new DefaultMessageWriter(secretKey),
ServiceLifetime.Transient);
services.Add(descriptor);
De inbyggda Add{LIFETIME}
metoderna använder samma metod. Se till exempel AddScoped-källkoden.
Konstruktorinmatningsbeteende
Tjänster kan lösas med hjälp av:
- IServiceProvider
- ActivatorUtilities:
- Skapar objekt som inte är registrerade i containern.
- Används med vissa ramverksfunktioner.
Konstruktorer kan acceptera argument som inte tillhandahålls av beroendeinmatning, men argumenten måste tilldela standardvärden.
När tjänster löses av IServiceProvider
eller ActivatorUtilities
kräver konstruktorinmatning en offentlig konstruktor.
När tjänster löses av ActivatorUtilities
kräver konstruktorinmatning att det bara finns en tillämplig konstruktor. Konstruktoröverlagring stöds, men det finns bara en överlagring vars argument kan uppfyllas genom beroendeinmatning.
Omfångsverifiering
När appen körs i Development
miljön och anropar CreateApplicationBuilder för att skapa värden utför standardtjänstleverantören kontroller för att kontrollera att:
- Begränsade tjänster matchas inte från rottjänstleverantören.
- Begränsade tjänster matas inte in i singletons.
Rottjänstprovidern skapas när BuildServiceProvider anropas. Rottjänstleverantörens livslängd motsvarar appens livslängd när providern börjar med appen och tas bort när appen stängs av.
Begränsade tjänster tas bort av containern som skapade dem. Om en begränsad tjänst skapas i rotcontainern höjs tjänstens livslängd effektivt till singleton eftersom den endast tas bort av rotcontainern när appen stängs av. Validering av tjänstomfattningar fångar upp dessa situationer när BuildServiceProvider
anropas.
Omfångsscenarier
IServiceScopeFactory är alltid registrerad som en singleton, men IServiceProvider kan variera beroende på livslängden för den innehållande klassen. Om du till exempel löser tjänster från ett omfång och någon av dessa tjänster tar en IServiceProvider, blir det en begränsad instans.
För att uppnå omfångstjänster inom implementeringar av IHostedService, till exempel BackgroundService, ska du inte mata in tjänstberoenden via konstruktorinmatning. Mata i stället in IServiceScopeFactory, skapa ett omfång och lös sedan beroenden från omfånget för att använda lämplig tjänstlivslängd.
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);
}
}
}
}
}
I föregående kod, medan appen körs, är bakgrundstjänsten:
- Beror på IServiceScopeFactory.
- Skapar en IServiceScope för att lösa ytterligare tjänster.
- Löser begränsade tjänster för förbrukning.
- Fungerar med att bearbeta objekt och sedan vidarebefordra dem och markerar dem slutligen som bearbetade.
I källkodsexemplet kan du se hur implementeringar av kan dra nytta av IHostedService begränsade tjänstlivslängder.
Nyckelade tjänster
Från och med .NET 8 finns det stöd för tjänstregistreringar och sökningar baserat på en nyckel, vilket innebär att det är möjligt att registrera flera tjänster med en annan nyckel och använda den här nyckeln för sökningen.
Tänk till exempel på det fall där du har olika implementeringar av gränssnittet IMessageWriter
: MemoryMessageWriter
och QueueMessageWriter
.
Du kan registrera dessa tjänster med hjälp av överbelastningen av tjänstregistreringsmetoderna (som vi såg tidigare) som stöder en nyckel som en parameter:
services.AddKeyedSingleton<IMessageWriter, MemoryMessageWriter>("memory");
services.AddKeyedSingleton<IMessageWriter, QueueMessageWriter>("queue");
key
är inte begränsat till string
, det kan vara vad object
du vill, så länge typen implementerar Equals
korrekt .
I konstruktorn för klassen som använder IMessageWriter
lägger du till FromKeyedServicesAttribute för att ange nyckeln för tjänsten som ska matchas:
public class ExampleService
{
public ExampleService(
[FromKeyedServices("queue")] IMessageWriter writer)
{
// Omitted for brevity...
}
}
Se även
- Förstå grunderna för beroendeinmatning i .NET
- Använda beroendeinmatning i .NET
- Riktlinjer för beroendeinmatning
- Beroendeinmatning i ASP.NET Core
- NDC-konferensmönster för DI-apputveckling
- Explicit beroendeprincip
- Inversion av kontrollcontainrar och beroendeinmatningsmönstret (Martin Fowler)
- DI-buggar ska skapas på github.com/dotnet/extensions lagringsplats