Sdílet prostřednictvím


injektáž závislostí jádra Blazor ASP.NET

Poznámka:

Toto není nejnovější verze tohoto článku. Aktuální verzi najdete v tomto článku ve verzi .NET 9.

Upozorňující

Tato verze ASP.NET Core se už nepodporuje. Další informace najdete v tématu .NET a .NET Core Zásady podpory. Aktuální verzi najdete ve verzi .NET 8 tohoto článku.

Důležité

Tyto informace se týkají předběžného vydání produktu, který může být podstatně změněn před komerčním vydáním. Microsoft neposkytuje žádné záruky, výslovné ani předpokládané, týkající se zde uváděných informací.

Aktuální verzi najdete v tomto článku ve verzi .NET 9.

Rainer Stropek a Mike Rousos

Tento článek vysvětluje, jak Blazor můžou aplikace vkládat služby do komponent.

Injektáž závislostí (DI) je technika pro přístup ke službám nakonfigurovaným v centrálním umístění:

  • Služby registrované architekturou je možné vložit přímo do Razor komponent.
  • Blazor aplikace definují a registrují vlastní služby a zpřístupňuje je v celé aplikaci prostřednictvím DI.

Poznámka:

Před čtením tohoto tématu doporučujeme přečíst injektáž závislostí v ASP.NET Core .

Výchozí služby

Služby uvedené v následující tabulce se běžně používají v Blazor aplikacích.

Služba Životnost Popis
HttpClient Rozsahem

Poskytuje metody pro odesílání požadavků HTTP a příjem odpovědí HTTP z prostředku identifikovaného identifikátorem URI.

Na straně klienta je aplikace HttpClient zaregistrovaná v Program souboru a používá prohlížeč pro zpracování provozu HTTP na pozadí.

Na straně HttpClient serveru není služba ve výchozím nastavení nakonfigurovaná jako služba. V kódu na straně serveru zadejte znak HttpClient.

Další informace najdete v tématu Volání webového rozhraní API z aplikace ASP.NET CoreBlazor.

Je HttpClient zaregistrovaná jako služba s vymezeným oborem, nikoli jako singleton. Další informace najdete v části Životnost služby.

IJSRuntime

Na straně klienta: Singleton

Na straně serveru: Vymezeno

Architektura Blazor se zaregistruje IJSRuntime v kontejneru služby aplikace.

Představuje instanci modulu runtime JavaScript, kde se odesílají volání JavaScriptu. Další informace najdete v tématu Volání funkcí JavaScriptu z metod .NET v ASP.NET Core Blazor.

Při hledání vložení služby do jedné služby na serveru použijte některý z následujících přístupů:

  • Změňte registraci služby tak, aby IJSRuntimeodpovídala registraci služby, což je vhodné v případě, že služba pracuje se stavem specifickým pro uživatele.
  • IJSRuntime Předejte implementaci singletonové služby jako argument volání metody namísto vložení do singletonu.
NavigationManager

Na straně klienta: Singleton

Na straně serveru: Vymezeno

Architektura Blazor se zaregistruje NavigationManager v kontejneru služby aplikace.

Obsahuje pomocné rutiny pro práci s identifikátory URI a stavem navigace. Další informace najdete v tématu Pomocné rutiny URI a navigačního stavu.

Další služby zaregistrované architekturou Blazor jsou popsány v dokumentaci, kde se používají k popisu Blazor funkcí, jako je konfigurace a protokolování.

Vlastní poskytovatel služeb automaticky neposkytuje výchozí služby uvedené v tabulce. Pokud používáte vlastního poskytovatele služeb a vyžadujete některou ze služeb zobrazených v tabulce, přidejte požadované služby novému poskytovateli služeb.

Přidání služeb na straně klienta

Nakonfigurujte služby pro kolekci služeb aplikace v Program souboru. V následujícím příkladu je implementace registrována ExampleDependency pro IExampleDependency:

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

await builder.Build().RunAsync();

Po sestavení hostitele jsou služby k dispozici z kořenového oboru DI předtím, než se vykreslí všechny komponenty. To může být užitečné pro spuštění logiky inicializace před vykreslením obsahu:

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();

Hostitel poskytuje centrální instanci konfigurace aplikace. Na základě předchozího příkladu se adresa URL služby weather předává z výchozího zdroje konfigurace (například appsettings.json) do 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();

Přidání služeb na straně serveru

Po vytvoření nové aplikace prozkoumejte část Program souboru:

var builder = WebApplication.CreateBuilder(args);

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

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

Proměnná builder představuje s objekty IServiceCollectionWebApplicationBuilder popisovače služby , což je seznam objektů popisovače služby. Služby se přidávají poskytováním popisovačů služby do kolekce služeb. Následující příklad ukazuje koncept s rozhraním IDataAccess a jeho konkrétní implementací DataAccess:

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

Po vytvoření nové aplikace prozkoumejte metodu Startup.ConfigureServices v Startup.cs:

using Microsoft.Extensions.DependencyInjection;

...

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

Metoda ConfigureServices je předána IServiceCollection, což je seznam popisovač služby objekty. Služby se do ConfigureServices metody přidají poskytnutím popisovačů služby kolekci služeb. Následující příklad ukazuje koncept s rozhraním IDataAccess a jeho konkrétní implementací DataAccess:

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

Registrace běžných služeb

Pokud se vyžaduje jedna nebo více běžných služeb na straně klienta a serveru, můžete umístit běžné registrace služeb na straně klienta metody a volat metodu pro registraci služeb v obou projektech.

Nejprve zachytáte běžné registrace služeb do samostatné metody. Vytvořte například metodu ConfigureCommonServices na straně klienta:

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

V případě souboru na straně Program klienta zavolejte ConfigureCommonServices , abyste zaregistrovali běžné služby:

var builder = WebAssemblyHostBuilder.CreateDefault(args);

...

ConfigureCommonServices(builder.Services);

V souboru na straně Program serveru zavolejte ConfigureCommonServices , abyste zaregistrovali běžné služby:

var builder = WebApplication.CreateBuilder(args);

...

Client.Program.ConfigureCommonServices(builder.Services);

Příklad tohoto přístupu najdete v tématu ASP.NET Dalších Blazor WebAssembly scénářích zabezpečení.

Služby na straně klienta, které selhávají během předdefinování

Tato část se vztahuje pouze na komponenty WebAssembly v s Blazor Web App.

Blazor Web Apps obvykle prerender client-side WebAssembly komponenty. Pokud je aplikace spuštěná s požadovanou službou zaregistrovanou pouze v .Client projektu, spuštění aplikace způsobí chybu za běhu, která se podobá následující, když se komponenta pokusí použít požadovanou službu během předrenderingu:

InvalidOperationException: Nelze zadat hodnotu {PROPERTY} typu {ASSEMBLY}}. Client.Pages. {NÁZEV SOUČÁSTI}. Neexistuje žádná registrovaná služba typu {SERVICE}.

Při řešení tohoto problému použijte některý z následujících přístupů:

  • Zaregistrujte službu v hlavním projektu, aby byla dostupná během předkreslování komponent.
  • Pokud se pro komponentu předběžné vykreslování nevyžaduje, zakažte předběžné vykreslování podle pokynů v režimu vykreslování ASP.NET CoreBlazor. Pokud tento přístup přijmete, nemusíte službu registrovat v hlavním projektu.

Další informace naleznete v tématu Služby na straně klienta se nepodařilo vyřešit během předdefinování.

Životnost služby

Služby je možné nakonfigurovat s životnostmi zobrazenými v následující tabulce.

Životnost Popis
Scoped

Na straně klienta aktuálně není koncept oborů DI. Scoped-registrované služby se chovají jako Singleton služby.

Vývoj na straně serveru podporuje Scoped životnost požadavků HTTP, ale ne mezi SignalR zprávami připojení nebo okruhu mezi komponentami načtenými v klientovi. Část Razor Pages nebo MVC aplikace zpracovává služby s vymezeným oborem za normálních okolností a při navigaci mezi stránkami nebo zobrazeními nebo zobrazením ke komponentě znovu vytvoří služby na každém požadavku HTTP. Omezené služby nejsou rekonstruovány při navigaci mezi komponentami v klientovi, kde probíhá komunikace se serverem přes připojení okruhu uživatele, ne prostřednictvím SignalR požadavků HTTP. V následujících scénářích komponent klienta jsou služby s vymezeným oborem rekonstruovány, protože se pro uživatele vytvoří nový okruh:

  • Uživatel zavře okno prohlížeče. Uživatel otevře nové okno a přejde zpět do aplikace.
  • Uživatel zavře kartu aplikace v okně prohlížeče. Uživatel otevře novou kartu a přejde zpět do aplikace.
  • Uživatel vybere tlačítko pro opětovné načtení nebo aktualizaci prohlížeče.

Další informace o zachování stavu uživatele v aplikacích na straně serveru najdete v tématu ASP.NET Správa stavu jádraBlazor.

Singleton Di vytvoří jednu instanci služby. Všechny komponenty, které vyžadují Singleton službu, obdrží stejnou instanci služby.
Transient Kdykoli komponenta získá instanci Transient služby z kontejneru služby, obdrží novou instanci služby.

Systém DI je založen na systému DI v ASP.NET Core. Další informace naleznete v tématu Injektáž závislostí v ASP.NET Core.

Vyžádání služby v komponentě

Pro vkládání služeb do komponent Blazor podporuje injektáž konstruktoru a injektáž vlastností.

Injektáž konstruktoru

Po přidání služeb do kolekce služeb vložte jednu nebo více služeb do komponent pomocí injektáže konstruktoru. Následující příklad vloží NavigationManager službu.

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");
    }
}

Injektáž vlastností

Po přidání služeb do kolekce služeb vložte jednu nebo více služeb do součástí direktivy @injectRazor , která má dva parametry:

  • Typ: Typ služby, která se má vložit.
  • Vlastnost: Název vlastnosti, která přijímá vloženou službu App Service. Vlastnost nevyžaduje ruční vytvoření. Kompilátor vytvoří vlastnost.

Další informace najdete v tématu Injektáž závislostí do zobrazení v ASP.NET Core.

K vložení různých služeb použijte více @inject příkazů.

Následující příklad ukazuje, jak použít direktivu @inject . Implementace služby Services.NavigationManager se vloží do vlastnosti Navigationkomponenty . Všimněte si, že kód používá NavigationManager pouze abstrakci.

PropertyInjection.razor:

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

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

Interně generovaná vlastnost (Navigation) používá [Inject] atribut. Obvykle se tento atribut nepoužívá přímo. Pokud je základní třída požadovaná pro komponenty a vložené vlastnosti jsou také vyžadovány pro základní třídu, přidejte atribut ručně[Inject]:

using Microsoft.AspNetCore.Components;

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

    ...
}

Poznámka:

Vzhledem k tomu, že se očekává, že vložené služby budou dostupné, je výchozí literál s operátorem null-forgiving (default!) přiřazen v .NET 6 nebo novějším. Další informace naleznete v tématu Referenční typy s možnou hodnotou null (NRT) a statická analýza stavu kompilátoru .NET s nulovým stavem.

V komponentách odvozených ze základní třídy není direktiva @inject nutná. Základní InjectAttribute třída je dostatečná. Komponenta vyžaduje pouze direktivu @inherits . V následujícím příkladu jsou pro komponentu Demo k dispozici všechny vložené službyCustomComponentBase:

@page "/demo"
@inherits CustomComponentBase

Použití DI ve službách

Složité služby můžou vyžadovat další služby. V následujícím příkladu vyžaduje DataAccess HttpClient výchozí službu. @inject (nebo [Inject] atribut) není k dispozici pro použití ve službách. Místo toho je nutné použít injektáž konstruktoru. Požadované služby se přidávají přidáním parametrů do konstruktoru služby. Když DI vytvoří službu, rozpozná služby, které vyžaduje v konstruktoru, a poskytne je odpovídajícím způsobem. V následujícím příkladu konstruktor obdrží via HttpClient DI. HttpClient je výchozí služba.

using System.Net.Http;

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

    ...
}

Injektáž konstruktoru se podporuje u primárních konstruktorů v jazyce C# 12 (.NET 8) nebo novějším:

using System.Net.Http;

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

Požadavky pro injektáž konstruktoru:

  • Jeden konstruktor musí existovat, jehož argumenty mohou být splněny di. Pokud zadají výchozí hodnoty, jsou povoleny další parametry, které nejsou pokryty diplatou.
  • Příslušný konstruktor musí být public.
  • Musí existovat jeden použitelný konstruktor. V případě nejednoznačnosti vyvolá funkce DI výjimku.

Vložení klíčových služeb do komponent

Blazor podporuje vkládání klíčových služeb pomocí atributu [Inject] . Klíče umožňují určení rozsahu registrace a spotřeby služeb při použití injektáže závislostí. InjectAttribute.Key Pomocí vlastnosti zadejte klíč, který má služba vložit:

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

Třídy základních komponent nástroje pro správu oboru DI

V jiných aplikacích nežBlazor ASP.NET Core se obory a přechodné služby obvykle vztahují na aktuální požadavek. Jakmile se požadavek dokončí, vyřaďte oborové a přechodné služby systémem DI.

V interaktivních aplikacích na straně Blazor serveru trvá rozsah DI po dobu trvání okruhu ( SignalR připojení mezi klientem a serverem), což může mít za následek omezené a jednorázové přechodné služby, které žijí mnohem déle než životnost jedné komponenty. Proto přímo nesouvejte do komponenty vymezenou službu, pokud máte v úmyslu životnost služby odpovídat životnosti komponenty. Přechodné služby vložené do komponenty, která neimplementuje IDisposable , jsou při uvolnění komponenty uvolněny z paměti. Vložené přechodné služby , které implementují IDisposable , jsou však udržovány kontejnerem DI po celou dobu životnosti okruhu, což brání uvolňování paměti služby při uvolnění komponenty a vede k nevracení paměti. Alternativní přístup k vymezeným službám založeným na OwningComponentBase typu je popsán dále v této části a jednorázové přechodné služby by se neměly vůbec používat. Další informace naleznete v tématu Návrh pro řešení přechodných jednorázových Blazor Server řešení na (dotnet/aspnetcore #26676).

I v aplikacích na straně Blazor klienta, které nepracují přes okruh, se služby zaregistrované s vymezenou životností považují za jednoúčelové, takže žijí déle než vymezené služby v typických aplikacích ASP.NET Core. Jednorázové přechodné služby na straně klienta také žijí déle než komponenty, do kterých se vkládat, protože kontejner DI, který obsahuje odkazy na jednorázové služby, trvá po celou dobu životnosti aplikace a brání uvolňování paměti ve službách. I když jsou dlouhodobé jednorázové přechodné služby na serveru větší obavy, měly by se jim vyhnout i registrace klientských služeb. Použití tohoto OwningComponentBase typu se také doporučuje pro služby na straně klienta k řízení životnosti služeb a jednorázové přechodné služby by se neměly vůbec používat.

Přístup, který omezuje životnost služby, je typ OwningComponentBase . OwningComponentBase je abstraktní typ odvozený z ComponentBase toho, že vytvoří obor DI odpovídající životnosti komponenty. Pomocí tohoto oboru může komponenta vkládat služby s vymezenou životností a mít je aktivní tak dlouho, dokud komponenta. Když je komponenta zničena, služby od poskytovatele služeb s vymezeným oborem komponenty jsou také uvolněny. To může být užitečné pro služby opakovaně používané v rámci komponenty, ale ne sdílené mezi komponentami.

K dispozici jsou dvě verze typu OwningComponentBase a popsány v následujících dvou částech:

OwningComponentBase

OwningComponentBase je abstraktní, uvolnitelná podřízená položka ComponentBase typu s chráněnou ScopedServices vlastností typu IServiceProvider. Zprostředkovatel lze použít k řešení služeb, které jsou vymezeny na dobu životnosti komponenty.

Služby DI vložené do komponenty pomocí @inject nebo [Inject] atribut nejsou vytvořeny v oboru komponenty. Chcete-li použít obor komponenty, musí být služby vyřešeny pomocí buď ScopedServices GetRequiredService nebo GetService. Všechny služby vyřešené pomocí ScopedServices poskytovatele mají své závislosti uvedené v oboru komponenty.

Následující příklad ukazuje rozdíl mezi vložením služby s vymezeným oborem přímo a překladem služby na ScopedServices serveru. Následující rozhraní a implementace pro třídu časových cest zahrnují DT vlastnost, která má obsahovat DateTime hodnotu. Volání implementace DateTime.Now , která se nastaví DT při TimeTravel vytvoření instance třídy.

ITimeTravel.cs:

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

TimeTravel.cs:

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

Služba je zaregistrovaná jako vymezená v souboru na straně Program serveru. Na straně serveru mají služby s vymezeným oborem životnost, která se rovná době trvání okruhu.

V souboru Program:

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

V následující komponentě TimeTravel:

  • Časová cestovní služba je přímo vložena @inject jako TimeTravel1.
  • Služba je také vyřešena samostatně s ScopedServices a GetRequiredService jako TimeTravel2.

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>();
    }
}

Při počátečním přechodu na komponentu TimeTravel se služba time travel service vytvoří při načítání komponenty dvakrát a TimeTravel1 TimeTravel2 má stejnou počáteční hodnotu:

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

Při přechodu mimo komponentu TimeTravel do jiné komponenty a zpět ke komponentě TimeTravel :

  • TimeTravel1 je poskytována stejná instance služby, která byla vytvořena při prvním načtení komponenty, takže hodnota DT zůstává stejná.
  • TimeTravel2 získá novou ITimeTravel instanci služby s TimeTravel2 novou hodnotou DT.

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

TimeTravel1 je svázaný s okruhem uživatele, který zůstane nedotčený a není uvolněn, dokud se základní okruh nevykonstruuje. Služba je například uvolněna, pokud je okruh odpojen pro dobu uchovávání odpojeného okruhu.

Navzdory omezené registraci služby v Program souboru a dlouhověkost okruhu uživatele obdrží novou ITimeTravel instanci služby pokaždé, TimeTravel2 když je komponenta inicializována.

OwningComponentBase<TService>

OwningComponentBase<TService> odvozuje a OwningComponentBase přidává Service vlastnost, která vrací instanci T z oboru zprostředkovatele DI. Tento typ představuje pohodlný způsob přístupu ke službám s vymezeným oborem bez použití instance IServiceProvider , kdy aplikace vyžaduje z kontejneru DI pomocí oboru komponenty jednu primární službu. Tato ScopedServices vlastnost je dostupná, takže aplikace může v případě potřeby získat služby jiných typů.

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

Detekce přechodných jednorázových použití na straně klienta

Vlastní kód lze přidat do aplikace na straně Blazor klienta, aby bylo možné detekovat jednorázové přechodné služby v aplikaci, která by měla používat OwningComponentBase. Tento přístup je užitečný, pokud máte obavy, že kód přidaný do aplikace v budoucnu využívá jednu nebo více přechodných jednorázových služeb, včetně služeb přidaných knihovnami. Ukázkový kód je k dispozici v Blazor ukázkovém úložišti GitHubu (jak stáhnout).

V .NET 6 nebo novějších verzích ukázky BlazorSample_WebAssembly zkontrolujte následující:

  • DetectIncorrectUsagesOfTransientDisposables.cs
  • Services/TransientDisposableService.cs
  • V Program.cs:
    • Obor názvů aplikace Services je k dispozici v horní části souboru (using BlazorSample.Services;).
    • DetectIncorrectUsageOfTransients je volána ihned po builder přiřazení z WebAssemblyHostBuilder.CreateDefault.
    • Je TransientDisposableService registrován (builder.Services.AddTransient<TransientDisposableService>();).
    • EnableTransientDisposableDetection se volá na integrovaném hostiteli v kanálu zpracování aplikace (host.EnableTransientDisposableDetection();).
  • Aplikace zaregistruje TransientDisposableService službu bez vyvolání výjimky. Pokus o vyřešení služby vyvolá TransientService.razor při pokusu InvalidOperationException o vytvoření instance TransientDisposableServicerozhraní .

Detekce přechodných jednorázových prostředků na straně serveru

Vlastní kód lze přidat do aplikace na straně serveru, aby bylo možné detekovat jednorázové přechodné služby na straně Blazor serveru v aplikaci, která by měla používat OwningComponentBase. Tento přístup je užitečný, pokud máte obavy, že kód přidaný do aplikace v budoucnu využívá jednu nebo více přechodných jednorázových služeb, včetně služeb přidaných knihovnami. Ukázkový kód je k dispozici v Blazor ukázkovém úložišti GitHubu (jak stáhnout).

V .NET 8 nebo novějších verzích ukázky BlazorSample_BlazorWebApp zkontrolujte následující:

Ve verzích BlazorSample_Server ukázky .NET 6 nebo .NET 7 zkontrolujte následující:

  • DetectIncorrectUsagesOfTransientDisposables.cs
  • Services/TransitiveTransientDisposableDependency.cs:
  • V Program.cs:
    • Obor názvů aplikace Services je k dispozici v horní části souboru (using BlazorSample.Services;).
    • DetectIncorrectUsageOfTransients je volána v tvůrci hostitelů (builder.DetectIncorrectUsageOfTransients();).
    • Služba TransientDependency je zaregistrovaná (builder.Services.AddTransient<TransientDependency>();).
    • Je TransitiveTransientDisposableDependency registrován pro ITransitiveTransientDisposableDependency (builder.Services.AddTransient<ITransitiveTransientDisposableDependency, TransitiveTransientDisposableDependency>();).
  • Aplikace zaregistruje TransientDependency službu bez vyvolání výjimky. Pokus o vyřešení služby vyvolá TransientService.razor při pokusu InvalidOperationException o vytvoření instance TransientDependencyrozhraní .

Přechodné registrace služby pro IHttpClientFactory/HttpClient obslužné rutiny

Doporučuje se registrace přechodných služeb pro IHttpClientFactory/HttpClient obslužné rutiny. Pokud aplikace obsahuje IHttpClientFactory/HttpClient obslužné rutiny a používá IRemoteAuthenticationBuilder<TRemoteAuthenticationState,TAccount> k přidání podpory ověřování, zjistí se také následující přechodné jedno použití pro ověřování na straně klienta, které se očekává a může se ignorovat:

Byly zjištěny IHttpClientFactory/HttpClient také další instance. Tyto instance je také možné ignorovat.

Blazor Ukázkové aplikace v Blazor úložišti GitHub s ukázkami (jak stáhnout) předvádějí kód pro detekci přechodných jednorázových použití. Kód se ale deaktivuje, protože ukázkové aplikace obsahují IHttpClientFactory/HttpClient obslužné rutiny.

Aktivace ukázkového kódu a určující jeho operaci:

  • Odkomentujte přechodné jednorázové čáry v Program.cs.

  • Odeberte podmíněné vrácení se změnami NavLink.razor , které brání TransientService zobrazení komponenty v navigačním bočním panelu aplikace:

    - else if (name != "TransientService")
    + else
    
  • Spusťte ukázkovou aplikaci a přejděte na komponentu TransientService na adrese /transient-service.

Použití Entity Framework Core (EF Core) DbContext z DI

Další informace najdete v tématu ASP.NET Core Blazor s Entity Framework Core (EF Core).

Přístup ke službám na straně Blazor serveru z jiného oboru DI

Obslužné rutiny aktivit okruhu poskytují přístup k vymezeným Blazor službám z jinýchBlazor oborů injektáže závislostí (DI), jako jsou obory vytvořené pomocí IHttpClientFactory.

Před vydáním ASP.NET Core v .NET 8 se přístup ke službám vymezeným okruhem z jiných oborů injektáže závislostí vyžadovaných pomocí vlastního základního typu komponenty. U obslužných rutin aktivit okruhu se nevyžaduje vlastní základní typ komponenty, jak ukazuje následující příklad:

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;
    }
}

Přístup ke službám v rozsahu okruhu vložením požadovaného CircuitServicesAccessor místa.

Příklad, který ukazuje, jak získat přístup k AuthenticationStateProvider nastavení DelegatingHandler pomocí IHttpClientFactory, viz Server-side ASP.NET Core Blazor další scénáře zabezpečení.

Někdy může dojít k tomu, že komponenta Razor vyvolá asynchronní metody, které spouští kód v jiném oboru DI. Bez správného přístupu nemají tyto obory DI přístup ke Blazorslužbám, jako IJSRuntime jsou například a Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage.

Například HttpClient instance vytvořené pomocí IHttpClientFactory mají vlastní obor služby DI. V důsledku toho HttpMessageHandler instance nakonfigurované na zařízení HttpClient nejsou schopné přímo vkládat Blazor služby.

Vytvořte třídu BlazorServiceAccessor , která definuje AsyncLocal, který ukládá BlazorIServiceProvider pro aktuální asynchronní kontext. Instanci BlazorServiceAccessor je možné získat z jiného oboru služby DI pro přístup ke službám Blazor .

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; }
    }
}

Pokud chcete nastavit hodnotu BlazorServiceAccessor.Services automaticky při async vyvolání metody komponenty, vytvořte vlastní základní komponentu, která znovu implementuje tři primární asynchronní vstupní body do Razor kódu komponenty:

Následující třída demonstruje implementaci základní komponenty.

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;
        }
    }
}

Všechny komponenty rozšiřující se CustomComponentBase automaticky nastavily BlazorServiceAccessor.Services na IServiceProvider aktuální Blazor obor DI.

Nakonec do Program souboru přidejte BlazorServiceAccessor službu s vymezeným oborem:

builder.Services.AddScoped<BlazorServiceAccessor>();

Nakonec přidejte Startup.ConfigureServices Startup.csslužbu BlazorServiceAccessor jako vymezenou službu:

services.AddScoped<BlazorServiceAccessor>();

Další materiály