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


ASP.NET Core Blazor függőséginjektálás

Megjegyzés:

Ez nem a cikk legújabb verziója. Az aktuális kiadásról a cikk .NET 10-es verziójában olvashat.

Figyelmeztetés

A ASP.NET Core ezen verziója már nem támogatott. További információt a .NET és a .NET Core támogatási szabályzatában talál. A jelen cikk .NET 9-es verzióját lásd az aktuális kiadásért .

Rainer Stropek és Mike Rousos

Ez a cikk azt ismerteti, hogy az alkalmazások hogyan Blazor injektálhatnak szolgáltatásokat az összetevőkbe.

A függőséginjektálás (DI) egy központi helyen konfigurált szolgáltatások elérésére szolgáló technika:

  • A keretrendszer által regisztrált szolgáltatások közvetlenül az összetevőkbe Razor injektálhatók.
  • Blazor az alkalmazások definiálják és regisztrálják az egyéni szolgáltatásokat, és elérhetővé teszik őket az alkalmazás teljes területén a DI-en keresztül.

Megjegyzés:

Javasoljuk, hogy a témakör elolvasása előtt olvassa el a Függőséginjektálást a ASP.NET Core-ban .

Alapértelmezett szolgáltatások

Az alábbi táblázatban látható szolgáltatásokat gyakran használják az alkalmazások.Blazor

Szolgáltatás Élettartam Leírás
HttpClient Hatókör

Metódusokat biztosít a HTTP-kérések küldéséhez és a HTTP-válaszok fogadásához egy URI által azonosított erőforrástól.

Ügyféloldalon a HttpClient példányt regisztrálja az alkalmazás a Program fájlban, és a böngésző segítségével kezeli a HTTP-forgalmat a háttérben.

Kiszolgálóoldalon egy HttpClient alapértelmezés szerint nincs szolgáltatásként konfigurálva. A kiszolgálóoldali kódban adjon meg egy HttpClient.

További információ: Webes API meghívása egy ASP.NET Core-alkalmazásbólBlazor.

A HttpClient rendszer hatókörön belüli szolgáltatásként van regisztrálva, nem önállóan. További információkért tekintse meg a szolgáltatás élettartamáról szóló szakaszt.

IJSRuntime

Ügyféloldali: Singleton

Kiszolgálóoldal: Hatókör

A Blazor keretrendszer regisztrál IJSRuntime az alkalmazás szolgáltatástárolójában.

Egy JavaScript-futtatókörnyezet példányát jelöli, ahol JavaScript-hívásokat hajtanak végre. További információ: JavaScript-függvények meghívása a .NET-metódusokból ASP.NET Core Blazor.

Amikor a szolgáltatást a kiszolgálón található egyetlen szolgáltatásba szeretné injektálni, használja az alábbi módszerek egyikét:

  • Módosítsa a szolgáltatásregisztráció hatókörét úgy, hogy megfeleljen IJSRuntimea regisztrációnak, ami akkor megfelelő, ha a szolgáltatás felhasználóspecifikus állapottal foglalkozik.
  • Adjátok át a IJSRuntime-t a singleton szolgáltatás implementációjának metódushívásaihoz argumentumként, ahelyett, hogy a singletonba injektálnátok.
NavigationManager

Ügyféloldali: Singleton

Kiszolgálóoldal: Hatókör

A Blazor keretrendszer regisztrál NavigationManager az alkalmazás szolgáltatástárolójában.

Az URI-k és a navigációs állapotok használatát segítő segédeket tartalmaz. További információt az URI és a navigációs állapot súgójában talál.

A Blazor keretrendszer által regisztrált további szolgáltatásokat a dokumentáció ismerteti, ahol azokat olyan funkciók ismertetésére használják, mint a konfiguráció és a naplózás.

Az egyéni szolgáltató nem biztosítja automatikusan a táblázatban felsorolt alapértelmezett szolgáltatásokat. Ha egyéni szolgáltatót használ, és a táblázatban látható szolgáltatások bármelyikét igényli, adja hozzá a szükséges szolgáltatásokat az új szolgáltatóhoz.

Ügyféloldali szolgáltatások hozzáadása

Konfigurálja az alkalmazás szolgáltatásgyűjteményének szolgáltatásait a Program fájlban. Az alábbi példában regisztráljuk a ExampleDependency megvalósítást a IExampleDependency számára.

var builder = WebAssemblyHostBuilder.CreateDefault(args);
...
builder.Services.AddSingleton<IExampleDependency, ExampleDependency>();
...

await builder.Build().RunAsync();

A gazdagép felépítése után a szolgáltatások a gyökér DI-hatókörtől elérhetők, mielőtt bármilyen összetevő megjelenítésre kerülne. Ez hasznos lehet az inicializálási logika futtatásához a tartalom renderelése előtt:

var builder = WebAssemblyHostBuilder.CreateDefault(args);
...
builder.Services.AddSingleton<WeatherService>();
...

var host = builder.Build();

var weatherService = host.Services.GetRequiredService<WeatherService>();
await weatherService.InitializeWeatherAsync();

await host.RunAsync();

A gazdagép központi konfigurációs példányt biztosít az alkalmazáshoz. Az előző példára építve a weather service URL-címe egy alapértelmezett konfigurációs forrásból (például appsettings.json) lesz átadva a következőnek InitializeWeatherAsync:

var builder = WebAssemblyHostBuilder.CreateDefault(args);
...
builder.Services.AddSingleton<WeatherService>();
...

var host = builder.Build();

var weatherService = host.Services.GetRequiredService<WeatherService>();
await weatherService.InitializeWeatherAsync(
    host.Configuration["WeatherServiceUrl"]);

await host.RunAsync();

Kiszolgálóoldali szolgáltatások hozzáadása

Új alkalmazás létrehozása után vizsgálja meg a Program fájl egy részét:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();

A builder változó egy WebApplicationBuilderIServiceCollection reprezentál, amely egy szolgáltatásleíró objektumok listája. A szolgáltatások a szolgáltatásgyűjteményhez szolgáltatásleírók hozzáadásával bővülnek. Az alábbi példa a felülettel és annak IDataAccess konkrét megvalósításával DataAccesskapcsolatos fogalmat mutatja be:

builder.Services.AddSingleton<IDataAccess, DataAccess>();

Új alkalmazás létrehozása után vizsgálja meg a Startup.ConfigureServices metódust a következő helyen: Startup.cs

using Microsoft.Extensions.DependencyInjection;

...

public void ConfigureServices(IServiceCollection services)
{
    ...
}

A ConfigureServices metódus átad egy IServiceCollection, a szolgáltatásleíró objektumok listáját tartalmazó listát. A szolgáltatásleírók szolgáltatásgyűjteményhez ConfigureServices való biztosításával vehetők fel a szolgáltatások a metódusba. Az alábbi példa a felülettel és annak IDataAccess konkrét megvalósításával DataAccesskapcsolatos fogalmat mutatja be:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IDataAccess, DataAccess>();
}

Gyakori szolgáltatások regisztrálása

Ha egy vagy több gyakori szolgáltatás szükséges ügyfél- és kiszolgálóoldali, a közös szolgáltatásregisztrációkat elhelyezheti egy ügyféloldali metódusban, és meghívhatja a metódust, hogy mindkét projektben regisztrálja a szolgáltatásokat.

Először is vegye figyelembe a gyakori szolgáltatásregisztrációkat egy külön metódusban. Hozzon létre például egy metódust ConfigureCommonServices ügyféloldalon:

public static void ConfigureCommonServices(IServiceCollection services)
{
    services.Add...;
}

Az ügyféloldali Program fájlhoz hívja meg ConfigureCommonServices a közös szolgáltatások regisztrálásához.

var builder = WebAssemblyHostBuilder.CreateDefault(args);

...

ConfigureCommonServices(builder.Services);

A kiszolgálóoldali Program fájlban használja az ConfigureCommonServices-t a közös szolgáltatások regisztrálásához.

var builder = WebApplication.CreateBuilder(args);

...

Client.Program.ConfigureCommonServices(builder.Services);

Erre a megközelítésre példaként tekintse meg a ASP.NET Core Blazor WebAssembly további biztonsági forgatókönyveit.

Ügyféloldali szolgáltatások, amelyek az előrendezés során meghiúsulnak

Ez a szakasz csak az s webassembly összetevőire Blazor Web Appvonatkozik.

Blazor Web Appáltalában előrerendeli az ügyféloldali WebAssembly-összetevőket. Ha egy alkalmazás csak a .Client projektben regisztrált kötelező szolgáltatással fut, az alkalmazás végrehajtása a következőhöz hasonló futásidejű hibát eredményez, amikor egy összetevő megkísérli használni a szükséges szolgáltatást az előrendelés során:

InvalidOperationException: Nem adható meg {PROPERTY} érték a(z) "{ASSEMBLY}} típuson. Client.Pages. {COMPONENT NAME}". Nincs {SERVICE} típusú regisztrált szolgáltatás.

A probléma megoldásához használja az alábbi módszerek egyikét :

  • Regisztrálja a szolgáltatást a fő projektben, hogy elérhetővé tegye az összetevő-előrendelés során.
  • Ha az összetevőhöz nincs szükség előzetes rendezésre, tiltsa le az előrendelést a Prerender ASP.NET Core-összetevők Razor útmutatását követve. Ha ezt a megközelítést alkalmazza, nem kell regisztrálnia a szolgáltatást a fő projektben.

További információért lásd a prerenderálás során nem megoldható ügyféloldali szolgáltatások szakaszt a Prerenderálás cikkben, amely a Blazor dokumentáció későbbi részében jelenik meg.

A szolgáltatás élettartama

A szolgáltatások az alábbi táblázatban látható élettartammal konfigurálhatók.

Élettartam Leírás
Scoped

Az ügyféloldaliak jelenleg nem rendelkeznek a DI-hatókörök fogalmával. Scoped-regisztrált szolgáltatások úgy viselkednek, mint a Singleton szolgáltatások.

A kiszolgálóoldali fejlesztés támogatja a Scoped HTTP-kérések élettartamát, de az ügyféloldalon betöltött összetevők közötti kapcsolati/áramköri üzeneteket nem támogatja SignalR. Az Razor alkalmazás Lapok vagy MVC része általában kezeli a hatókörön belüli szolgáltatásokat, és minden HTTP-kérelemben újra létrehozza a szolgáltatásokat, amikor lapok vagy nézetek között, illetve egy lapról vagy nézetről egy összetevőre navigál. A hatókörön belüli szolgáltatások nem rekonstruálhatók, amikor az ügyfél összetevői között navigálnak, ahol a kiszolgálóval való kommunikáció a SignalR felhasználó kapcsolatcsoportjának kapcsolatán keresztül történik, nem HTTP-kéréseken keresztül. A következő komponens forgatókönyvek során az ügyfélnél a hatókörrel rendelkező szolgáltatások újraépülnek, mert a felhasználó számára egy új kör jön létre.

  • A felhasználó bezárja a böngésző ablakát. A felhasználó megnyit egy új ablakot, és vissza lép az alkalmazáshoz.
  • A felhasználó bezárja az alkalmazás egy lapját egy böngészőablakban. A felhasználó megnyitja az új lapot, és vissza lép az alkalmazáshoz.
  • A felhasználó kiválasztja a böngésző újrabetöltési/frissítési gombját.

A felhasználói állapot kiszolgálóoldali alkalmazásokban való megőrzéséről további információt a ASP.NET Core-állapotkezelés Blazor áttekintésében és ASP.NET Core Blazor kiszolgálóoldali állapotkezelésében talál.

Singleton A DI a szolgáltatás egyetlen példányát hozza létre. A szolgáltatást igénylő Singleton összes összetevő ugyanazt a szolgáltatáspéldányt kapja.
Transient Amikor egy összetevő lekért egy szolgáltatáspéldányt Transient a szolgáltatástárolóból, megkapja a szolgáltatás új példányát .

A DI-rendszer a ASP.NET Core direndszerén alapul. További információért lásd: Függőséginjektálás az ASP.NET Core-ban.

Szolgáltatás kérése egy összetevőben

A szolgáltatások összetevőkbe történő injektálásához támogatja a Blazorkonstruktorinjektálást és a tulajdonságinjektálást.

Konstruktor injektálás

Miután hozzáadta a szolgáltatásokat a szolgáltatásgyűjteményhez, injektáljon egy vagy több szolgáltatást az összetevőkbe konstruktorinjektálással. Az alábbi példa beszúrja a NavigationManager szolgáltatást.

ConstructorInjection.razor:

@page "/constructor-injection"

<button @onclick="HandleClick">
    Take me to the Counter component
</button>

ConstructorInjection.razor.cs:

using Microsoft.AspNetCore.Components;

public partial class ConstructorInjection(NavigationManager navigation)
{
    private void HandleClick()
    {
        navigation.NavigateTo("/counter");
    }
}

Tulajdonságinjektálás

Miután hozzáadta a szolgáltatásokat a szolgáltatásgyűjteményhez, injektáljon egy vagy több szolgáltatást az összetevőkbe az @injectRazor irányelvvel, amelynek két paramétere van:

  • Típus: Az injektálandó szolgáltatás típusa.
  • A tulajdonság: Az injektált app-szolgáltatást fogadó tulajdonság neve. A tulajdonság nem igényel manuális létrehozást. A fordító létrehozza a tulajdonságot.

További információ: Függőség injektálás nézetekbe az ASP.NET Core-ban.

Több @inject utasítás használatával injektálhat különböző szolgáltatásokat.

Az alábbi példa bemutatja és szemlélteti a @inject irányelv használatát. Az Services.NavigationManager szolgáltatást az összetevő Navigation tulajdonságába injektálják. Figyelje meg, hogy a kód csak az absztrakciót NavigationManager használja.

PropertyInjection.razor:

@page "/property-injection"
@inject NavigationManager Navigation

<button @onclick="@(() => Navigation.NavigateTo("/counter"))">
    Take me to the Counter component
</button>

A létrehozott tulajdonság (Navigation) belsőleg az [Inject] attribútumot használja. Ezt az attribútumot általában nem használják közvetlenül. Ha az összetevőkhöz alaposztályra van szükség, és az alaposztályhoz injektált tulajdonságokra is szükség van, adja hozzá manuálisan az [Inject] attribútumot:

using Microsoft.AspNetCore.Components;

public class ComponentBase : IComponent
{
    [Inject]
    protected NavigationManager Navigation { get; set; } = default!;

    ...
}

Megjegyzés:

Mivel az injektált szolgáltatások várhatóan elérhetők lesznek, a null-megbocsátó operátorral (default!) rendelkező alapértelmezett literál a .NET 6-os vagy újabb verziójában lesz hozzárendelve. További információ: Null értékű referenciatípusok (NRT-k) és .NET-fordító nullállapotú statikus elemzése.

Az alaposztályból származó összetevők esetében nincs szükség az @inject irányelvre. Az InjectAttribute alaposztály elegendő. Az összetevőnek csak az irányelvre @inherits van szüksége. Az alábbi példában a CustomComponentBase bármely injektált szolgáltatása elérhető a Demo összetevő számára.

@page "/demo"
@inherits CustomComponentBase

Szolgáltatásinjektálás felső szintű importálási fájlon keresztül (_Imports.razor)

Ez a szakasz csak Blazor Web Apps-re vonatkozik.

A Components mappában (Components/_Imports.razor) lévő legfelső szintű importálási fájl a mappahierarchiában lévő összes összetevőbe injektálja a hivatkozásokat, beleértve a App összetevőt (App.razor). Az App összetevő mindig statikusan jelenik meg, még akkor is, ha az oldalösszetevő előzetes beállítása le van tiltva. Ezért a szolgáltatások felső szintű importfájl által történő injektálása esetén a szolgáltatás két példányának feloldására kerül sor a lap komponenseiben.

A forgatókönyv megoldásához injektálja a szolgáltatást a Pages mappába (Components/Pages/_Imports.razor) helyezett új importálási fájlba. Erről a helyről a szolgáltatás csak egyszer oldódik fel az oldalösszetevőkben.

A DI használata a szolgáltatásokban

Az összetett szolgáltatások további szolgáltatásokat igényelhetnek. Az alábbi példában DataAccess az HttpClient alapértelmezett szolgáltatásra van szükség. @inject (vagy az [Inject] attribútum) nem érhető el a szolgáltatásokban való használatra. Ehelyett konstruktorinjektálást kell alkalmazni. A szükséges szolgáltatások a szolgáltatás konstruktorához paraméterek hozzáadásával vehetők fel. Amikor a DI létrehozza a szolgáltatást, felismeri a konstruktorban szükséges szolgáltatásokat, és ennek megfelelően biztosítja őket. Az alábbi példában a konstruktor kap egy HttpClient di-en keresztül. HttpClient egy alapértelmezett szolgáltatás.

using System.Net.Http;

public class DataAccess : IDataAccess
{
    public DataAccess(HttpClient http)
    {
        ...
    }

    ...
}

A konstruktorinjektálás a C# 12 (.NET 8) vagy újabb verzióiban támogatott elsődleges konstruktorokkal :

using System.Net.Http;

public class DataAccess(HttpClient http) : IDataAccess
{
    ...
}

Konstruktorinjektálás előfeltételei:

  • Egy konstruktornak léteznie kell, amelynek argumentumait a DI mind teljesíteni tudja. A DI által nem érintett további paraméterek akkor engedélyezettek, ha alapértelmezett értékeket adnak meg.
  • A megfelelő konstruktornak a következőnek kell lennie public: .
  • Egy alkalmazható konstruktor szükséges. Kétértelműség esetén a DI kivételt jelez.

Kulcsos szolgáltatások injektálása összetevőkbe

Blazor támogatja kulcsszolgáltatások injektálását a [Inject] attribútum használatával. A kulcsok lehetővé teszik a regisztráció és a szolgáltatások használatának hatókörét függőséginjektálás használatakor. InjectAttribute.Key A tulajdonság használatával adja meg a szolgáltatás injektálandó kulcsát:

[Inject(Key = "my-service")]
public IMyService MyService { get; set; }

Segédeszköz-alapösszetevő-osztályok di-hatókör kezeléséhez

A nem ASP.NET Core-alkalmazásokbanBlazor a hatókörrel rendelkező és az átmeneti szolgáltatások általában az aktuális kérelemre terjednek ki. A kérelem befejezése után a hatókörön belüli és átmeneti szolgáltatásokat a DI-rendszer megsemmisíti.

Az interaktív kiszolgálóoldali Blazor alkalmazásokban a DI-hatókör a kapcsolatcsoport ( SignalR az ügyfél és a kiszolgáló közötti kapcsolat) időtartamára tart, ami azt eredményezheti, hogy a hatókörrel rendelkező és az eldobható átmeneti szolgáltatások sokkal hosszabb ideig élnek, mint egy összetevő élettartama. Ezért ne injektáljon közvetlenül hatókörrel rendelkező szolgáltatást egy összetevőbe, ha azt szeretné, hogy a szolgáltatás élettartama igazodjon az összetevő élettartamához. A nem implementált IDisposable összetevőkbe injektált átmeneti szolgáltatások szemétként lesznek összegyűjtve az összetevő ártalmatlanításakor. Az injektált átmeneti szolgáltatásokat IDisposableazonban a DI-tároló tartja fenn a kapcsolatcsoport teljes élettartama alatt, ami megakadályozza a szolgáltatás szemétgyűjtését az összetevő ártalmatlanításakor, és memóriavesztést eredményez. A típuson OwningComponentBase alapuló hatókörű szolgáltatások alternatív megközelítését a jelen szakasz későbbi részében ismertetjük, és az eldobható átmeneti szolgáltatásokat egyáltalán nem szabad használni. További információért lásd: Tervezés az átmeneti megoldások kezelésére Blazor Server (dotnet/aspnetcore #26676).

Még a nem kapcsolat-alapú ügyféloldali Blazor alkalmazások esetében is az olyan szolgáltatások, amelyek hatókörrel rendelkező élettartammal vannak regisztrálva, singletonként vannak kezelve, így hosszabb ideig élnek, mint a hatókörrel rendelkező szolgáltatások a tipikus ASP.NET Core-alkalmazásokban. Az ügyféloldali eldobható átmeneti szolgáltatások is hosszabb ideig élnek, mint azok az összetevők, amelyekbe injektálják őket, mivel az eldobható szolgáltatásokra hivatkozó DI-tároló az alkalmazás teljes élettartama alatt megmarad, megakadályozva a szolgáltatások szemétgyűjtését. Bár a hosszú élettartamú, eldobható átmeneti szolgáltatások nagyobb aggodalomra adnak okot a kiszolgálón, ezeket is el kell kerülni ügyfélszolgáltatás-regisztrációként. OwningComponentBase A típus használata az ügyféloldali hatókörű szolgáltatások esetében is ajánlott a szolgáltatás élettartamának szabályozásához, és az eldobható átmeneti szolgáltatásokat egyáltalán nem szabad használni.

A szolgáltatás élettartamát korlátozni kívánt megközelítés a OwningComponentBase típus használata. OwningComponentBase egy olyan absztrakt típus, amely az ComponentBaseösszetevő élettartamának megfelelő DI-hatókört hoz létre. Ennek a hatókörnek a használatával az összetevők a hatókörön belüli élettartammal rendelkező szolgáltatásokat injektálhatják, és mindaddig életben tarthatják őket, amíg az összetevőt használják. Ha az összetevő megsemmisül, az összetevő hatókörébe tartozó szolgáltatótól származó szolgáltatások is el lesznek távolítva. Ez hasznos lehet az összetevőkben újrahasznált, de az összetevők között nem megosztott szolgáltatások esetében.

A következő két szakaszban két típusverzió OwningComponentBase érhető el és ismertethető:

OwningComponentBase

OwningComponentBase egy absztrakt, eldobható gyermek, amelynek típusa ComponentBase védett ScopedServices tulajdonsággal rendelkezik IServiceProvider. A szolgáltató az összetevő élettartamára vonatkozó hatókörű szolgáltatások megoldására használható.

Az összetevőbe @inject injektált DI-szolgáltatások vagy az [Inject] attribútum nem jön létre az összetevő hatókörében. Az összetevő hatókörének használatához a szolgáltatásokat a következővel ScopedServices kell feloldani: vagy GetRequiredServiceGetService. A ScopedServices szolgáltató által megoldott szolgáltatások esetében a függőségek az összetevő hatókörében kerülnek biztosításra.

Az alábbi példa azt mutatja be, hogy mi a különbség a hatókörrel rendelkező szolgáltatás közvetlen injektálása és a kiszolgálón ScopedServices metódus alkalmazásával történő feloldása között. Az időutazási osztály alábbi felülete és implementációja tartalmaz egy DT tulajdonságot, amely egy DateTime értéket tart. Amikor a DateTime.Now osztály példányosításra kerül, az implementáció meghívja a DT-t, hogy beállítsa a TimeTravel-et.

ITimeTravel.cs:

public interface ITimeTravel
{
    public DateTime DT { get; set; }
}

TimeTravel.cs:

public class TimeTravel : ITimeTravel
{
    public DateTime DT { get; set; } = DateTime.Now;
}

A szolgáltatás hatókörként van regisztrálva a kiszolgálóoldali Program fájlban. A kiszolgálóoldali hatókörű szolgáltatások élettartama megegyezik a kapcsolatcsoport időtartamával.

A Program fájlban:

builder.Services.AddScoped<ITimeTravel, TimeTravel>();

Az alábbi TimeTravel összetevőben:

  • Az időutazási szolgáltatást közvetlenül a(z) @inject segítségével injektálják TimeTravel1-ként.
  • A szolgáltatás külön-külön megvalósul ScopedServices és GetRequiredService segítségével TimeTravel2 is.

TimeTravel.razor:

@page "/time-travel"
@inject ITimeTravel TimeTravel1
@inherits OwningComponentBase

<h1><code>OwningComponentBase</code> Example</h1>

<ul>
    <li>TimeTravel1.DT: @TimeTravel1?.DT</li>
    <li>TimeTravel2.DT: @TimeTravel2?.DT</li>
</ul>

@code {
    private ITimeTravel TimeTravel2 { get; set; } = default!;

    protected override void OnInitialized()
    {
        TimeTravel2 = ScopedServices.GetRequiredService<ITimeTravel>();
    }
}
@page "/time-travel"
@inject ITimeTravel TimeTravel1
@inherits OwningComponentBase

<h1><code>OwningComponentBase</code> Example</h1>

<ul>
    <li>TimeTravel1.DT: @TimeTravel1?.DT</li>
    <li>TimeTravel2.DT: @TimeTravel2?.DT</li>
</ul>

@code {
    private ITimeTravel TimeTravel2 { get; set; } = default!;

    protected override void OnInitialized()
    {
        TimeTravel2 = ScopedServices.GetRequiredService<ITimeTravel>();
    }
}

Az összetevőre való első navigáláskor TimeTravel, az időutazási szolgáltatás kétszer lesz példányosítva a komponens betöltésekor, és TimeTravel1 és TimeTravel2 ugyanazzal a kezdeti értékkel rendelkezik:

TimeTravel1.DT: 8/31/2022 2:54:45 PM
TimeTravel2.DT: 8/31/2022 2:54:45 PM

Amikor az összetevőről TimeTravel egy másik összetevőre navigál, és visszalép az TimeTravel összetevőre:

  • TimeTravel1 ugyanazt a szolgáltatáspéldányt adja meg, amelyet az összetevő első betöltésekor hoztak létre, így az érték DT változatlan marad.
  • TimeTravel2 új ITimeTravel szolgáltatáspéldányt kap TimeTravel2 új DT értékkel.

TimeTravel1.DT: 8/31/2022 2:54:45 PM
TimeTravel2.DT: 8/31/2022 2:54:48 PM

TimeTravel1 a felhasználó kapcsolatcsoportjához van kötve, amely érintetlen marad, és nem lesz megsemmisítve, amíg az alapul szolgáló kapcsolatcsoportot fel nem bontják. A szolgáltatás például akkor szűnik meg, ha az áramkör meg van szakítva a megszakított áramkör megőrzési időszaka alatt.

A Program fájlban található hatóköralapú szolgáltatásregisztráció és a felhasználói kör hosszú fennmaradásának ellenére a TimeTravel2 minden egyes összetevő inicializálásakor új ITimeTravel szolgáltatáspéldányt kap.

OwningComponentBase<TService>

OwningComponentBase<TService> OwningComponentBase-ból származik és hozzáad egy Service tulajdonságot, amely visszaad egy T példányt a hatókörnél meghatározott DI-szolgáltatóból. Ez a típus kényelmes módja annak, hogy a hatókörrel rendelkező szolgáltatásokat elérje anélkül, hogy példányt használna IServiceProvider, amikor az alkalmazásnak csak egy elsődleges szolgáltatásra van szüksége a DI-tárolóból az összetevő saját hatókörének használatával. A ScopedServices tulajdonság elérhető, így az alkalmazás szükség esetén más típusú szolgáltatásokat is igénybe vehet.

@page "/users"
@attribute [Authorize]
@inherits OwningComponentBase<AppDbContext>

<h1>Users (@Service.Users.Count())</h1>

<ul>
    @foreach (var user in Service.Users)
    {
        <li>@user.UserName</li>
    }
</ul>

Ügyféloldali átmeneti erőforrások észlelése

Az ügyféloldali Blazor alkalmazásokhoz egyéni kód adható hozzá, amely észleli az eldobható átmeneti szolgáltatásokat egy olyan alkalmazásban, amelyet használnia OwningComponentBasekell. Ez a megközelítés akkor hasznos, ha úgy véli, hogy az alkalmazáshoz a jövőben hozzáadott kód egy vagy több átmeneti eldobható szolgáltatást használ, beleértve a kódtárak által hozzáadott szolgáltatásokat is. A bemutató kód a Blazor GitHub-adattár mintáiban érhető el (a letöltés módja).

Vizsgálja meg a következőt a minta .NET 6-os vagy újabb verzióiban BlazorSample_WebAssembly :

  • DetectIncorrectUsagesOfTransientDisposables.cs
  • Services/TransientDisposableService.cs
  • In Program.cs:
    • Az alkalmazás Services névtere a fájl (using BlazorSample.Services;) tetején található.
    • DetectIncorrectUsageOfTransients közvetlenül azután hívódik meg, hogy a builder hozzárendelése a WebAssemblyHostBuilder.CreateDefault-ból/-ből történik.
    • A TransientDisposableService regisztrálva van (builder.Services.AddTransient<TransientDisposableService>();).
    • EnableTransientDisposableDetection az alkalmazás (host.EnableTransientDisposableDetection();) feldolgozási folyamatában lévő beépített gazdagépen van meghívva.
  • Az alkalmazás kivétel nélkül regisztrálja a TransientDisposableService szolgáltatást. A szolgáltatás TransientService.razor megoldásának megkísérlése azonban egy olyan eseményt InvalidOperationException eredményez, amikor a keretrendszer megpróbál létrehozni egy példányt.TransientDisposableService

A kiszolgálóoldali átmeneti eldobható objektumok észlelése

A kiszolgálóoldali Blazor alkalmazásokhoz egyéni kód adható hozzá, amely észleli az ideiglenes szolgáltatásokat egy olyan alkalmazásban, amelynek OwningComponentBase szolgáltatást kell használnia. Ez a megközelítés akkor hasznos, ha úgy véli, hogy az alkalmazáshoz a jövőben hozzáadott kód egy vagy több átmeneti eldobható szolgáltatást használ, beleértve a kódtárak által hozzáadott szolgáltatásokat is. A bemutató kód a Blazor GitHub-adattár mintáiban érhető el (a letöltés módja).

Vizsgálja meg a következőt a minta .NET 8 vagy újabb verzióiban BlazorSample_BlazorWebApp :

Vizsgálja meg a következőket a minta .NET 6 vagy .NET 7 verzióiban BlazorSample_Server :

  • DetectIncorrectUsagesOfTransientDisposables.cs
  • Services/TransitiveTransientDisposableDependency.cs:
  • In Program.cs:
    • Az alkalmazás Services névtere a fájl (using BlazorSample.Services;) tetején található.
    • DetectIncorrectUsageOfTransients meghívásra kerül a gazdagép-építőn (builder.DetectIncorrectUsageOfTransients();).
    • A TransientDependency szolgáltatás regisztrálva van (builder.Services.AddTransient<TransientDependency>();).
    • A TransitiveTransientDisposableDependency rendszer regisztrálva van (ITransitiveTransientDisposableDependencybuilder.Services.AddTransient<ITransitiveTransientDisposableDependency, TransitiveTransientDisposableDependency>();).
  • Az alkalmazás kivétel nélkül regisztrálja a TransientDependency szolgáltatást. A szolgáltatás TransientService.razor megoldásának megkísérlése azonban egy olyan eseményt InvalidOperationException eredményez, amikor a keretrendszer megpróbál létrehozni egy példányt.TransientDependency

Átmeneti szolgáltatásregisztrációk kezelők számára IHttpClientFactory/HttpClient

A kezelők átmeneti IHttpClientFactory/HttpClient szolgáltatásregisztrációi ajánlottak. Ha az alkalmazás IHttpClientFactory/HttpClient kezelőket tartalmaz, és a IRemoteAuthenticationBuilder<TRemoteAuthenticationState,TAccount> használatával támogatja a hitelesítést, az ügyféloldali hitelesítéshez az alábbi eldobható átmeneti eszközök is felfedezhetők, amelyeket várhatóan figyelmen kívül kell hagyni.

Más példányok is fel vannak derítve IHttpClientFactory/HttpClient. Ezek az esetek szintén figyelmen kívül hagyhatók.

A BlazorMinták GitHub-adattárában találhatóBlazor mintaalkalmazások (a letöltési útmutató) bemutatják az átmeneti eldobható fájlok észlelésére szolgáló kódot. A kód azonban inaktiválva van, mert a mintaalkalmazások kezelőket tartalmaznak IHttpClientFactory/HttpClient .

A bemutató kód aktiválása és a művelet tanúsítása:

  • Az átmeneti, eldobható sorokat kommentelje ki a Program.cs.

  • Távolítsa el a feltételes bejelentkezést NavMenu.razor , amely megakadályozza, hogy az TransientService összetevő megjelenjen az alkalmazás navigációs oldalsávján:

    - && (c.Name != "TransientService")
    
  • Futtassa a mintaalkalmazást, és a TransientService összetevőhöz lépjen a /transient-service helyen.

A BlazorMinták GitHub-adattárában találhatóBlazor mintaalkalmazások (a letöltési útmutató) bemutatják az átmeneti eldobható fájlok észlelésére szolgáló kódot. Futtassa a mintaalkalmazást, és a TransientService összetevőhöz lépjen a /transient-service helyen.

Entity Framework Core (EF Core) DbContext használata a DI-ből

További információ: ASP.NET Core Blazor együtt az Entity Framework Core-mal (EF Core).

Kiszolgálóoldali Blazor szolgáltatások elérése egy másik DI-hatókörből

A kapcsolatcsoport-tevékenységkezelők olyan megközelítést biztosítanak, amely lehetővé teszik a hatóköralapú Blazor szolgáltatások elérését más, nem függőséginjektálási (DI-Blazor ) hatókörökből, például a használatával IHttpClientFactorylétrehozott hatókörökből.

A .NET 8-ban, az ASP.NET Core kiadása előtt, a circuit-scoped szolgáltatások eléréséhez más függőséginjektálási hatókörökből szükség volt egyéni alapösszetevő-típus használatára. A kapcsolatcsoport-tevékenységkezelők esetében nincs szükség egyéni alapösszetevő-típusra, ahogy az alábbi példa is mutatja:

public class CircuitServicesAccessor
{
    static readonly AsyncLocal<IServiceProvider> blazorServices = new();

    public IServiceProvider? Services
    {
        get => blazorServices.Value;
        set => blazorServices.Value = value!;
    }
}

public class ServicesAccessorCircuitHandler(
    IServiceProvider services, CircuitServicesAccessor servicesAccessor) 
    : CircuitHandler
{
    public override Func<CircuitInboundActivityContext, Task> CreateInboundActivityHandler(
        Func<CircuitInboundActivityContext, Task> next) => 
            async context =>
            {
                servicesAccessor.Services = services;
                await next(context);
                servicesAccessor.Services = null;
            };
}

public static class CircuitServicesServiceCollectionExtensions
{
    public static IServiceCollection AddCircuitServicesAccessor(
        this IServiceCollection services)
    {
        services.AddScoped<CircuitServicesAccessor>();
        services.AddScoped<CircuitHandler, ServicesAccessorCircuitHandler>();

        return services;
    }
}

Használja a AddCircuitServicesAccessor hívását az alkalmazás Program fájljában:

builder.Services.AddCircuitServicesAccessor();

Az áramkör hatókörű szolgáltatások elérése a CircuitServicesAccessor szükséges helyre történő injektálásával.

Egy példa arra, hogyan érheti el a AuthenticationStateProvider-t a DelegatingHandler használatával beállított IHttpClientFactory-en keresztül, tekintse meg az ASP.NET Core szerveroldali és a Blazor Web App további biztonsági forgatókönyveit.

Előfordulhat, hogy egy Razor összetevő olyan aszinkron metódusokat hív meg, amelyek egy másik DI-hatókörben futtatják a kódot. A megfelelő megközelítés nélkül ezek a DI hatókörök nem férnek hozzá Blazor szolgáltatásaihoz, mint IJSRuntime és Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage.

Például a HttpClient segítségével létrehozott példányok saját DI-szolgáltatás-hatókörrel rendelkeznek IHttpClientFactory. Ennek eredményeképpen a HttpMessageHandler-en konfigurált HttpClient példányok nem tudják közvetlenül injektálni a Blazor szolgáltatásokat.

Hozzon létre egy BlazorServiceAccessor osztályt, amely definiál egy AsyncLocal-et, ami tárolja az aktuális aszinkron környezet BlazorIServiceProvider-ját. Egy BlazorServiceAccessor példány egy másik DI-szolgáltatás hatóköréből szerezhető be a Blazor szolgáltatások eléréséhez.

BlazorServiceAccessor.cs:

internal sealed class BlazorServiceAccessor
{
    private static readonly AsyncLocal<BlazorServiceHolder> s_currentServiceHolder = new();

    public IServiceProvider? Services
    {
        get => s_currentServiceHolder.Value?.Services;
        set
        {
            if (s_currentServiceHolder.Value is { } holder)
            {
                // Clear the current IServiceProvider trapped in the AsyncLocal.
                holder.Services = null;
            }

            if (value is not null)
            {
                // Use object indirection to hold the IServiceProvider in an AsyncLocal
                // so it can be cleared in all ExecutionContexts when it's cleared.
                s_currentServiceHolder.Value = new() { Services = value };
            }
        }
    }

    private sealed class BlazorServiceHolder
    {
        public IServiceProvider? Services { get; set; }
    }
}

Ahhoz, hogy egy BlazorServiceAccessor.Services összetevőmetódus meghívásakor a async értéke automatikusan beállításra kerüljön, hozzon létre egy egyéni alapösszetevőt, amely újraimplementálja a három elsődleges aszinkron belépési pontot az Razor összetevőkódba.

Az alábbi osztály az alapösszetevő implementálását mutatja be.

CustomComponentBase.cs:

using Microsoft.AspNetCore.Components;

public class CustomComponentBase : ComponentBase, IHandleEvent, IHandleAfterRender
{
    private bool hasCalledOnAfterRender;

    [Inject]
    private IServiceProvider Services { get; set; } = default!;

    [Inject]
    private BlazorServiceAccessor BlazorServiceAccessor { get; set; } = default!;

    public override Task SetParametersAsync(ParameterView parameters)
        => InvokeWithBlazorServiceContext(() => base.SetParametersAsync(parameters));

    Task IHandleEvent.HandleEventAsync(EventCallbackWorkItem callback, object? arg)
        => InvokeWithBlazorServiceContext(() =>
        {
            var task = callback.InvokeAsync(arg);
            var shouldAwaitTask = task.Status != TaskStatus.RanToCompletion &&
                task.Status != TaskStatus.Canceled;

            StateHasChanged();

            return shouldAwaitTask ?
                CallStateHasChangedOnAsyncCompletion(task) :
                Task.CompletedTask;
        });

    Task IHandleAfterRender.OnAfterRenderAsync()
        => InvokeWithBlazorServiceContext(() =>
        {
            var firstRender = !hasCalledOnAfterRender;
            hasCalledOnAfterRender |= true;

            OnAfterRender(firstRender);

            return OnAfterRenderAsync(firstRender);
        });

    private async Task CallStateHasChangedOnAsyncCompletion(Task task)
    {
        try
        {
            await task;
        }
        catch
        {
            if (task.IsCanceled)
            {
                return;
            }

            throw;
        }

        StateHasChanged();
    }

    private async Task InvokeWithBlazorServiceContext(Func<Task> func)
    {
        try
        {
            BlazorServiceAccessor.Services = Services;
            await func();
        }
        finally
        {
            BlazorServiceAccessor.Services = null;
        }
    }
}

Az CustomComponentBase-t kiterjesztő összetevőknek automatikusan be van állítva a BlazorServiceAccessor.Services az aktuális IServiceProvider DI-hatókörben Blazor.

Végül adja hozzá a Program fájlban a BlazorServiceAccessor hatókörhöz tartozó szolgáltatást:

builder.Services.AddScoped<BlazorServiceAccessor>();

Végül, a Startup.ConfigureServices elemet a Startup.cs-ben adja hozzá mint hatókörön belüli szolgáltatást a BlazorServiceAccessor-ként.

services.AddScoped<BlazorServiceAccessor>();

További erőforrások