.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 , az Worker osztályt módosítani kell.
  • Ha MessageWriter függőségek vannak, az osztálynak is konfigurálnia kell őket Worker . Egy nagy, több osztályt tartalmazó MessageWriterprojektben 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ával MessageWriter .
  • 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 az IMessageWriter 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.

LoggingMessageWriterILogger<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 class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;

    public Worker(ILogger<Worker> logger) =>
        _logger = logger;

    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:

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 Életre
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
Ártalmatlanítá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. Ez 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 IMessageWritera .

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ért lásd:

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 IMessageWriter2regisztrá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 ServiceDescriptorszolgá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:

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 ActivatorUtilitiesa konstruktorinjektáláshoz nyilvános konstruktor szükséges.

A szolgáltatások feloldása ActivatorUtilitieseseté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 IMessageWriterkü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 stringtetszőlegesre, object amíg a típus megfelelően implementál Equals.

Az osztály IMessageWriterkonstruktorá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