injektáž závislostí jádra Blazor ASP.NET
Poznámka:
Toto není nejnovější verze tohoto článku. Aktuální verzi najdete ve verzi .NET 8 tohoto článku.
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 ve verzi .NET 8 tohoto článku.
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 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ů:
|
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. Vývoj na straně serveru podporuje
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 @inject
Razor , 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 Navigation
komponenty . 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
jakoTimeTravel1
. - 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 hodnotaDT
zůstává stejná.TimeTravel2
získá novouITimeTravel
instanci služby sTimeTravel2
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 pobuilder
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();
).
- Obor názvů aplikace
- 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í instanceTransientDisposableService
rozhraní .
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 proITransitiveTransientDisposableDependency
(builder.Services.AddTransient<ITransitiveTransientDisposableDependency, TransitiveTransientDisposableDependency>();
).
- Obor názvů aplikace
- 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í instanceTransientDependency
rozhraní .
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.cs
službu BlazorServiceAccessor
jako vymezenou službu:
services.AddScoped<BlazorServiceAccessor>();