Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
A .NET támogatja a függőséginjektálási (DI) szoftvertervezési mintát, amely az osztályok és függőségeik közötti vezérlési inverzió (IoC) elérésének technikája. A függőséginjektálás a .NET-ben a keretrendszer beépített része, a konfigurációval, a naplózással és a beállítási mintával együtt.
A függőség olyan objektum, amelytől egy másik objektum függ. Vizsgálja meg a következő MessageWriter
osztályt egy Write
olyan módszerrel, amelytől más osztályok függenek:
public class MessageWriter
{
public void Write(string message)
{
Console.WriteLine($"MessageWriter.Write(message: \"{message}\")");
}
}
Az osztály létrehozhatja az MessageWriter
osztály egy példányát a metódusa használatához Write
. Az alábbi példában az MessageWriter
osztály az osztály függősége Worker
:
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);
}
}
}
Az osztály létrehozza és közvetlenül az MessageWriter
osztálytól függ. A szigorúan kódolt függőségek, például az előző példában, problémásak, és a következő okok miatt kerülendők:
- Ha egy másik implementációra szeretne cserélni
MessageWriter
, azWorker
osztályt módosítani kell. - Ha
MessageWriter
függőségek vannak, az osztálynak is konfigurálnia kell őketWorker
. Egy nagy, több osztályt tartalmazóMessageWriter
projektben a konfigurációs kód szétszóródik az alkalmazásban. - Ezt a megvalósítást nehéz egyesíteni. Az alkalmazásnak egy makett- vagy csonkosztályt
MessageWriter
kell használnia, ami ebben a megközelítésben nem lehetséges.
A függőséginjektálás a következő módon oldja meg ezeket a problémákat:
- Egy interfész vagy alaposztály használata a függőségi implementáció absztrakciójára.
- A függőség regisztrálása egy szolgáltatástárolóban. A .NET egy beépített szolgáltatástárolót biztosít. IServiceProvider A szolgáltatások általában az alkalmazás indításakor vannak regisztrálva, és hozzá vannak fűzve egy IServiceCollection. Az összes szolgáltatás hozzáadása után létre kell BuildServiceProvider hoznia a szolgáltatástárolót.
- A szolgáltatás injektálása annak az osztálynak a konstruktorába, ahol a szolgáltatást használják. A keretrendszer felelősséget vállal a függőség egy példányának létrehozásáért és eltávolításáért, ha már nincs rá szükség.
Példaként az IMessageWriter
interfész határozza meg a metódust Write
:
namespace DependencyInjection.Example;
public interface IMessageWriter
{
void Write(string message);
}
Ezt az interfészt egy konkrét típus valósítja meg: MessageWriter
namespace DependencyInjection.Example;
public class MessageWriter : IMessageWriter
{
public void Write(string message)
{
Console.WriteLine($"MessageWriter.Write(message: \"{message}\")");
}
}
A mintakód regisztrálja a szolgáltatást a IMessageWriter
konkrét típussal MessageWriter
. A AddSingleton metódus egyetlen élettartammal, az alkalmazás élettartamával regisztrálja a szolgáltatást.
A szolgáltatás élettartamát a cikk későbbi részében ismertetjük.
using DependencyInjection.Example;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();
builder.Services.AddSingleton<IMessageWriter, MessageWriter>();
using IHost host = builder.Build();
host.Run();
Az előző kódban a mintaalkalmazás:
Létrehoz egy gazdagépalkalmazás-szerkesztőpéldányt.
A szolgáltatásokat a következő regisztrációval konfigurálja:
- Az
Worker
üzemeltetett szolgáltatás. További információ: Worker Services in .NET. - Az
IMessageWriter
interfész önálló szolgáltatásként az osztály megfelelő implementációjávalMessageWriter
.
- Az
Létrehozza és futtatja a gazdagépet.
A gazdagép tartalmazza a függőséginjektálási szolgáltatót. Emellett tartalmazza az összes többi kapcsolódó szolgáltatást is, amely az automatikus példányosításhoz és a Worker
megfelelő IMessageWriter
implementáció argumentumként való biztosításához szükséges.
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);
}
}
}
A DI-minta használatával a feldolgozó szolgáltatás:
- Nem használja a konkrét típust
MessageWriter
, csak azIMessageWriter
azt megvalósító felületet. Ez megkönnyíti a feldolgozó szolgáltatás által használt implementáció módosítását a feldolgozó szolgáltatás módosítása nélkül. - Nem hoz létre példányt.
MessageWriter
A példányt a DI-tároló hozza létre.
A felület implementálása a IMessageWriter
beépített naplózási API-val javítható:
namespace DependencyInjection.Example;
public class LoggingMessageWriter(
ILogger<LoggingMessageWriter> logger) : IMessageWriter
{
public void Write(string message) =>
logger.LogInformation("Info: {Msg}", message);
}
A frissített AddSingleton
módszer regisztrálja az új IMessageWriter
implementációt:
builder.Services.AddSingleton<IMessageWriter, LoggingMessageWriter>();
A HostApplicationBuilder (builder
) típus a Microsoft.Extensions.Hosting
NuGet-csomag része.
LoggingMessageWriter
ILogger<TCategoryName>attól függ, hogy melyiket kéri a konstruktorban.
ILogger<TCategoryName>
egy keretrendszer által biztosított szolgáltatás.
Nem szokatlan a függőséginjektálás láncolt módon történő használata. Minden kért függőség saját függőségeket kér. A tároló feloldja a gráf függőségeit, és visszaadja a teljes mértékben feloldott szolgáltatást. A feloldandó függőségek együttes készletét általában függőségfának, függőségi gráfnak vagy objektumdiagramnak nevezzük.
A tároló feloldja ILogger<TCategoryName>
a nyitott (általános) típusok előnyeit, így nincs szükség minden (általános) létrehozott típus regisztrálására.
A függőséginjektálás terminológiájával egy szolgáltatás:
- Általában olyan objektum, amely szolgáltatást nyújt más objektumoknak, például a
IMessageWriter
szolgáltatásnak. - Nem kapcsolódik webszolgáltatáshoz, bár a szolgáltatás webszolgáltatást is használhat.
A keretrendszer robusztus naplózási rendszert biztosít. Az IMessageWriter
előző példákban bemutatott implementációk az alapszintű naplózás bemutatására, nem pedig a naplózás implementálására készültek. A legtöbb alkalmazásnak nem kell naplózókat írnia. Az alábbi kód bemutatja az alapértelmezett naplózás használatát, amely csak üzemeltetett Worker
szolgáltatásként AddHostedServicevaló regisztrációt igényel:
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);
}
}
}
Az előző kód használatával nincs szükség a Program.cs frissítésére, mert a naplózást a keretrendszer biztosítja.
Több konstruktor-felderítési szabály
Ha egy típus több konstruktort határoz meg, a szolgáltató logikája határozza meg, hogy melyik konstruktort használja. A legtöbb paraméterrel rendelkező konstruktor, ahol a típusok DI-feloldhatóak, ki van választva. Tekintse meg a következő C#-példaszolgáltatást:
public class ExampleService
{
public ExampleService()
{
}
public ExampleService(ILogger<ExampleService> logger)
{
// omitted for brevity
}
public ExampleService(FooService fooService, BarService barService)
{
// omitted for brevity
}
}
Az előző kódban feltételezzük, hogy a naplózás hozzá lett adva, és feloldható a szolgáltatótól, de a típusok és FooService
a BarService
típusok nem. A rendszer a ILogger<ExampleService>
paraméterrel rendelkező konstruktort használja a ExampleService
példány feloldásához. Annak ellenére, hogy van egy konstruktor, amely több paramétert határoz meg, az és FooService
a BarService
típusok nem feloldhatók di-feloldható.
Ha a konstruktorok felderítésekor kétértelműség áll fenn, a rendszer kivételt jelez. Tekintse meg a következő C#-példaszolgáltatást:
public class ExampleService
{
public ExampleService()
{
}
public ExampleService(ILogger<ExampleService> logger)
{
// omitted for brevity
}
public ExampleService(IOptions<ExampleOptions> options)
{
// omitted for brevity
}
}
Figyelmeztetés
A ExampleService
nem egyértelmű DI-feloldható típusparaméterekkel rendelkező kód kivételt eredményezne. Ne tegye ezt – a "nem egyértelmű DI-feloldható típusok" jelentésének megjelenítésére szolgál.
Az előző példában három konstruktor található. Az első konstruktor paraméter nélküli, és nem igényel szolgáltatást a szolgáltatótól. Tegyük fel, hogy a naplózás és a beállítások is hozzáadva lettek a DI-tárolóhoz, és di-feloldható szolgáltatások. Amikor a DI-tároló megpróbálja feloldani a ExampleService
típust, kivételt fog eredményezni, mivel a két konstruktor nem egyértelmű.
Elkerülheti a kétértelműséget egy olyan konstruktor definiálásával, amely mindkét DI-feloldható típust elfogadja:
public class ExampleService
{
public ExampleService()
{
}
public ExampleService(
ILogger<ExampleService> logger,
IOptions<ExampleOptions> options)
{
// omitted for brevity
}
}
Szolgáltatáscsoportok regisztrálása bővítménymetelyekkel
A Microsoft Extensions konvenciót használ a kapcsolódó szolgáltatások egy csoportjának regisztrálására. A konvenció egyetlen bővítménymetódus használatával Add{GROUP_NAME}
regisztrálja a keretrendszerfunkciók által igényelt összes szolgáltatást. A bővítménymetódus például AddOptions a beállítások használatához szükséges összes szolgáltatást regisztrálja.
Keretrendszer által biztosított szolgáltatások
Az elérhető gazdagép- vagy alkalmazásszerkesztő-minták használatakor az alapértelmezett értékeket alkalmazza a rendszer, és a keretrendszer regisztrálja a szolgáltatásokat. Vegye figyelembe a legnépszerűbb gazdagép- és alkalmazásszerkesztői mintákat:
- Host.CreateDefaultBuilder()
- Host.CreateApplicationBuilder()
- WebHost.CreateDefaultBuilder()
- WebApplication.CreateBuilder()
- WebAssemblyHostBuilder.CreateDefault
- MauiApp.CreateBuilder
Miután létrehozott egy szerkesztőt ezen API-k bármelyikéből, a IServiceCollection
keretrendszer által meghatározott szolgáltatások vannak meghatározva a gazdagép konfigurálásának módjától függően. A .NET-sablonokon alapuló alkalmazások esetében a keretrendszer több száz szolgáltatást regisztrálhat.
Az alábbi táblázat a keretrendszer által regisztrált szolgáltatások egy kis mintáját sorolja fel:
Szolgáltatás típusa | Élettartam |
---|---|
Microsoft.Extensions.DependencyInjection.IServiceScopeFactory | Egyedülálló |
IHostApplicationLifetime | Egyedülálló |
Microsoft.Extensions.Logging.ILogger<TCategoryName> | Egyedülálló |
Microsoft.Extensions.Logging.ILoggerFactory | Egyedülálló |
Microsoft.Extensions.ObjectPool.ObjectPoolProvider | Egyedülálló |
Microsoft.Extensions.Options.IConfigureOptions<TOptions> | Átmeneti |
Microsoft.Extensions.Options.IOptions<TOptions> | Egyedülálló |
System.Diagnostics.DiagnosticListener | Egyedülálló |
System.Diagnostics.DiagnosticSource | Egyedülálló |
Szolgáltatási élettartamok
A szolgáltatások a következő élettartamok egyikével regisztrálhatók:
A következő szakaszok az előző élettartamok mindegyikét ismertetik. Válasszon egy megfelelő élettartamot minden regisztrált szolgáltatáshoz.
Átmeneti
Az átmeneti élettartam-szolgáltatások minden alkalommal létrejönnek, amikor a szolgáltatástárolóból kérik őket. Ha átmenetiként szeretne regisztrálni egy szolgáltatást, hívja fel a .AddTransient
A kéréseket feldolgozó alkalmazásokban az átmeneti szolgáltatások a kérés végén lesznek megsemmisítve. Ez az élettartam minden kérelem-foglalást igénybe vesz, mivel a szolgáltatások minden alkalommal feloldódnak és létre vannak állítva. További információ: Függőséginjektálási irányelvek: IDisposable útmutató átmeneti és megosztott példányokhoz.
Hatókör
Webalkalmazások esetén a hatókörrel rendelkező élettartam azt jelzi, hogy a szolgáltatások ügyfélkérésenként (kapcsolatonként) egyszer jönnek létre. Hatókörön belüli szolgáltatások regisztrálása a AddScoped.
A kérelmeket feldolgozó alkalmazásokban a hatókörbe tartozó szolgáltatások a kérelem végén lesznek megsemmisítve.
Feljegyzés
Az Entity Framework Core használatakor a AddDbContext bővítménymetódus alapértelmezés szerint egy hatókörön belüli élettartamú típusokat DbContext
regisztrál.
A hatóköralapú szolgáltatást mindig a hatókörön belül kell használni – implicit hatókörből (például ASP.NET Core kérésenkénti hatóköréből), vagy egy explicit hatókörből, amely a hatókörrel IServiceScopeFactory.CreateScope()jött létre.
Ne oldjon fel egy hatókörrel rendelkező szolgáltatást közvetlenül egy singletonból sem konstruktorinjektálással, sem az önmagában való IServiceProvider kéréssel. Ennek hatására a hatókörrel rendelkező szolgáltatás egyetlentonnként viselkedik, ami helytelen állapothoz vezethet a későbbi kérések feldolgozásakor.
Ha expliciten hoz létre és használ egy hatókört a IServiceScopeFactory szolgáltatáshoz, akkor elfogadható, hogy egy singletonon belül oldódjon fel.
A következőket is érdemes:
- Önálló szolgáltatás feloldása hatókörrel rendelkező vagy átmeneti szolgáltatásból.
- Hatókörrel rendelkező szolgáltatás feloldása egy másik hatókörű vagy átmeneti szolgáltatásból.
Alapértelmezés szerint a fejlesztési környezetben a szolgáltatás feloldása egy másik szolgáltatásból hosszabb élettartammal kivételt eredményez. További információ: Hatókör érvényesítése.
Egyedülálló
Az egyszeri élettartamú szolgáltatások a következők:
- Az első alkalommal, amikor kérik őket.
- A fejlesztő egy implementációs példány közvetlenül a tárolónak való biztosításakor. Erre a megközelítésre ritkán van szükség.
A szolgáltatás implementációjának függőséginjektálási tárolóból történő minden további kérése ugyanazt a példányt használja. Ha az alkalmazás egyszeri működést igényel, engedélyezze a szolgáltatástároló számára a szolgáltatás élettartamának kezelését. Ne implementálja az egytonos kialakítási mintát, és adjon meg kódot a singleton elhelyezéséhez. A szolgáltatásokat soha nem szabad olyan kóddal megsemmisíteni, amely feloldotta a szolgáltatást a tárolóból. Ha egy típus vagy gyár egyetlentonként van regisztrálva, a tároló automatikusan megsemmisíti a singletont.
Singleton-szolgáltatások regisztrálása a AddSingleton. Az egyszálas szolgáltatásoknak biztonságosnak kell lenniük, és gyakran használják őket állapot nélküli szolgáltatásokban.
A kéréseket feldolgozó alkalmazásokban az egyszeri szolgáltatások az alkalmazás leállításakor ServiceProvider lesznek megsemmisítve. Mivel a memória csak az alkalmazás leállítása után szabadul fel, fontolja meg a memória egyszeri szolgáltatással való használatát.
Szolgáltatásregisztrációs módszerek
A keretrendszer olyan szolgáltatásregisztrációs bővítménymetszeteket biztosít, amelyek bizonyos helyzetekben hasznosak:
Metódus | Automatikus objektum megszabadulás |
Többszörös Megvalósítások |
Args átadása |
---|---|---|---|
Add{LIFETIME}<{SERVICE}, {IMPLEMENTATION}>() Példa: services.AddSingleton<IMyDep, MyDep>(); |
Igen | Igen | Nem |
Add{LIFETIME}<{SERVICE}>(sp => new {IMPLEMENTATION}) Példák: services.AddSingleton<IMyDep>(sp => new MyDep()); services.AddSingleton<IMyDep>(sp => new MyDep(99)); |
Igen | Igen | Igen |
Add{LIFETIME}<{IMPLEMENTATION}>() Példa: services.AddSingleton<MyDep>(); |
Igen | Nem | Nem |
AddSingleton<{SERVICE}>(new {IMPLEMENTATION}) Példák: services.AddSingleton<IMyDep>(new MyDep()); services.AddSingleton<IMyDep>(new MyDep(99)); |
Nem | Igen | Igen |
AddSingleton(new {IMPLEMENTATION}) Példák: services.AddSingleton(new MyDep()); services.AddSingleton(new MyDep(99)); |
Nem | Nem | Igen |
A típuselhelyezésről további információt a Szolgáltatások ártalmatlanítása című szakaszban talál.
A szolgáltatás csak implementációs típussal való regisztrálása egyenértékű azzal, ha a szolgáltatást ugyanazzal a megvalósítási és szolgáltatástípussal regisztrálja. Vegyük például a következő kódot:
services.AddSingleton<ExampleService>();
Ez egyenértékű azzal, hogy a szolgáltatást mind a szolgáltatással, mind az azonos típusú implementációval regisztrálja:
services.AddSingleton<ExampleService, ExampleService>();
Ez az egyenértékűség az oka annak, hogy egy szolgáltatás több implementációja nem regisztrálható olyan metódusokkal, amelyek nem használnak explicit szolgáltatástípust. Ezek a metódusok egy szolgáltatás több példányát is regisztrálhatják, de mindegyiknek ugyanaz a megvalósítási típusa lesz.
A fenti szolgáltatásregisztrációs módszerek bármelyike használható több azonos szolgáltatástípusú szolgáltatáspéldány regisztrálásához. Az alábbi példában AddSingleton
kétszer IMessageWriter
hívjuk meg szolgáltatástípusként. A második hívás felülbírálja AddSingleton
az előzőt a feloldáskor IMessageWriter
, és hozzáadja az előzőhöz, ha több szolgáltatás is feloldva IEnumerable<IMessageWriter>
van. A szolgáltatások a regisztrációjuk sorrendjében jelennek meg, amikor a feloldásuk folyamatban IEnumerable<{SERVICE}>
van.
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();
Az előző minta forráskódja IMessageWriter
a .
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);
}
}
A ExampleService
függvény két konstruktorparamétert határoz meg: egyetlen IMessageWriter
és egy IEnumerable<IMessageWriter>
. Az egyetlen IMessageWriter
az utolsó regisztrálandó implementáció, míg az IEnumerable<IMessageWriter>
összes regisztrált megvalósítást képviseli.
A keretrendszer bővítménymetelyeket is biztosít TryAdd{LIFETIME}
, amelyek csak akkor regisztrálják a szolgáltatást, ha még nincs regisztrálva implementáció.
Az alábbi példában a hívás AddSingleton
implementációként regisztrál ConsoleMessageWriter
a következőhöz IMessageWriter
: . A hívásnak TryAddSingleton
nincs hatása, mert IMessageWriter
már van regisztrált implementációja:
services.AddSingleton<IMessageWriter, ConsoleMessageWriter>();
services.TryAddSingleton<IMessageWriter, LoggingMessageWriter>();
A TryAddSingleton
műveletnek nincs hatása, mivel már hozzáadták, és a "kipróbálás" sikertelen lesz. A ExampleService
következőt állítja be:
public class ExampleService
{
public ExampleService(
IMessageWriter messageWriter,
IEnumerable<IMessageWriter> messageWriters)
{
Trace.Assert(messageWriter is ConsoleMessageWriter);
Trace.Assert(messageWriters.Single() is ConsoleMessageWriter);
}
}
További információk:
A TryAddEnumerable(ServiceDescriptor) metódusok csak akkor regisztrálják a szolgáltatást, ha még nincs ilyen típusú implementáció. A rendszer több szolgáltatást is felold IEnumerable<{SERVICE}>
. A szolgáltatások regisztrálásakor adjon hozzá egy példányt, ha az egyik ilyen típus még nem lett hozzáadva. A kódtár-szerzők TryAddEnumerable
használatával elkerülhető, hogy egy implementáció több példánya is regisztrálva legyen a tárolóban.
Az alábbi példában az első hívás, amely TryAddEnumerable
implementációként regisztrál MessageWriter
a következőhöz IMessageWriter1
: . A második hívás a következőre MessageWriter
regisztrálIMessageWriter2
: . A harmadik hívásnak nincs hatása, mert IMessageWriter1
már regisztrált implementációja van a következőnek 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>());
A szolgáltatásregisztráció általában sorrendfüggetlen, kivéve, ha több azonos típusú implementációt regisztrál.
IServiceCollection
objektumgyűjtemény ServiceDescriptor . Az alábbi példa bemutatja, hogyan regisztrálhat egy szolgáltatást egy ServiceDescriptor
szolgáltatás létrehozásával és hozzáadásával:
string secretKey = Configuration["SecretKey"];
var descriptor = new ServiceDescriptor(
typeof(IMessageWriter),
_ => new DefaultMessageWriter(secretKey),
ServiceLifetime.Transient);
services.Add(descriptor);
A beépített Add{LIFETIME}
metódusok ugyanazt a megközelítést használják. Lásd például az AddScoped forráskódot.
Konstruktorinjektálási viselkedés
A szolgáltatások az alábbiakkal oldhatók meg:
- IServiceProvider
-
ActivatorUtilities:
- Olyan objektumokat hoz létre, amelyek nincsenek regisztrálva a tárolóban.
- Néhány keretrendszerfunkcióval használható.
A konstruktorok elfogadhatják a függőséginjektálás által nem biztosított argumentumokat, de az argumentumoknak alapértelmezett értékeket kell hozzárendelniük.
Ha a szolgáltatásokat feloldjákIServiceProvider
, vagy ActivatorUtilities
a konstruktorinjektáláshoz nyilvános konstruktor szükséges.
A szolgáltatások feloldása ActivatorUtilities
esetén a konstruktorinjektáláshoz csak egy alkalmazható konstruktor szükséges. A konstruktor túlterhelések támogatottak, de csak egy túlterhelés létezhet, amelynek argumentumai függőséginjektálással mind teljesíthetők.
Hatókör érvényesítése
Amikor az alkalmazás a környezetben fut, és meghívja a Development
CreateApplicationBuildert a gazdagép létrehozásához, az alapértelmezett szolgáltató ellenőrzi, hogy:
- A hatókörön belüli szolgáltatások nem oldódnak fel a gyökérszolgáltatás-szolgáltatótól.
- A hatókörön belüli szolgáltatások nem injektálhatók egyetlen dobozba.
A főszolgáltatás-szolgáltató a meghíváskor BuildServiceProvider jön létre. A legfelső szintű szolgáltató élettartama az alkalmazás élettartamának felel meg, amikor a szolgáltató elindul az alkalmazással, és az alkalmazás leállításakor törlődik.
A hatókörön belüli szolgáltatásokat az őket létrehozó tároló dobja el. Ha egy hatókörrel rendelkező szolgáltatás jön létre a gyökértárolóban, a szolgáltatás élettartamát hatékonyan előlépteti egyetlen példányra, mert azt csak a gyökértároló dobja el, amikor az alkalmazás leáll. A szolgáltatás hatóköreinek érvényesítése a meghíváskor BuildServiceProvider
elkapja ezeket a helyzeteket.
Hatókör-forgatókönyvek
A IServiceScopeFactory rendszer mindig egyszeriként van regisztrálva, de ez az IServiceProvider adott osztály élettartamától függően változhat. Ha például egy hatókörből oldja fel a szolgáltatásokat, és ezek bármelyike igénybe vesz egy IServiceProviderszolgáltatást, az egy hatókörön belüli példány lesz.
Ha hatókörkezelési szolgáltatásokat szeretne elérni az olyan implementációkon IHostedServicebelül, mint például azBackgroundService, ne injektálja a szolgáltatásfüggőségeket konstruktorinjektálással. Ehelyett injektáljon IServiceScopeFactory, hozzon létre egy hatókört, majd oldja fel a hatókör függőségeit a megfelelő élettartam használatához.
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);
}
}
}
}
}
Az előző kódban, miközben az alkalmazás fut, a háttérszolgáltatás:
- Attól függ, hogy a IServiceScopeFactory.
- Létrehoz egy további IServiceScope szolgáltatások feloldásához.
- A hatókörön belüli szolgáltatások felhasználási célú feloldása.
- Dolgozik az objektumok feldolgozásán, majd átfektetésén, és végül megjelöli őket feldolgozottként.
A minta forráskódjából megtudhatja IHostedService , hogy az implementációk hogyan használhatják ki a hatókörön belüli szolgáltatási élettartamokat.
Kulcsos szolgáltatások
A .NET 8-tól kezdve a kulcson alapuló szolgáltatásregisztrációk és -keresések támogatottak, ami azt jelenti, hogy több szolgáltatást is regisztrálhat egy másik kulccsal, és ezt a kulcsot használhatja a kereséshez.
Vegyük például azt az esetet, amikor a felület IMessageWriter
különböző implementációi vannak: MemoryMessageWriter
és QueueMessageWriter
.
Ezeket a szolgáltatásokat a (korábban látott) szolgáltatásregisztrációs módszerek túlterhelésével regisztrálhatja, amelyek paraméterként támogatják a kulcsot:
services.AddKeyedSingleton<IMessageWriter, MemoryMessageWriter>("memory");
services.AddKeyedSingleton<IMessageWriter, QueueMessageWriter>("queue");
Ez key
nem korlátozódik a string
tetszőlegesre, object
amíg a típus megfelelően implementál Equals
.
Az osztály IMessageWriter
konstruktorában adja hozzá a FromKeyedServicesAttribute feloldani kívánt szolgáltatás kulcsát:
public class ExampleService
{
public ExampleService(
[FromKeyedServices("queue")] IMessageWriter writer)
{
// Omitted for brevity...
}
}
Lásd még
- A függőséginjektálás alapjai a .NET-ben
- Függőséginjektálás használata a .NET-ben
- Függőséginjektálási irányelvek
- Függőséginjektálás az ASP.NET Core-ban
- NDC konferenciaminták a DI-alkalmazások fejlesztéséhez
- Explicit függőségek elve
- Vezérlőtárolók inverziója és a függőségi injektálási minta (Martin Fowler)
- A DI-hibákat a github.com/dotnet/extensions adattárban kell létrehozni