Megosztás a következőn keresztül:


.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.

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:

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 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:

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 MessageWriterregisztrá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 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