.NET-függőséginjektálás
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 BarService
a FooService
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 BarService
a FooService
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.
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.
Feljegyzés
Ne oldjon fel hatókörön belüli szolgáltatást egyetlen helyről, és ügyeljen arra, hogy ne tegye meg közvetetten, például egy átmeneti szolgáltatáson keresztül. Ez azt okozhatja, hogy a szolgáltatás helytelen állapotú a későbbi kérések feldolgozásakor. A következőt é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 object 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 IMessageWriter2
regisztrálMessageWriter
: . 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