injeksi dependensi inti Blazor ASP.NET
Catatan
Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.
Peringatan
Versi ASP.NET Core ini tidak lagi didukung. Untuk informasi selengkapnya, lihat Kebijakan Dukungan .NET dan .NET Core. Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.
Penting
Informasi ini berkaitan dengan produk pra-rilis yang mungkin dimodifikasi secara substansial sebelum dirilis secara komersial. Microsoft tidak memberikan jaminan, tersirat maupun tersurat, sehubungan dengan informasi yang diberikan di sini.
Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.
Oleh Rainer Stropek dan Mike Rousos
Artikel ini menjelaskan bagaimana Blazor aplikasi dapat menyuntikkan layanan ke dalam komponen.
Injeksi dependensi (DI) adalah teknik untuk mengakses layanan yang dikonfigurasi di lokasi pusat:
- Layanan terdaftar kerangka kerja dapat disuntikkan langsung ke komponen Razor .
- Blazor aplikasi menentukan dan mendaftarkan layanan kustom dan membuatnya tersedia di seluruh aplikasi melalui DI.
Catatan
Sebaiknya baca injeksi Dependensi di ASP.NET Core sebelum membaca topik ini.
Layanan default
Layanan yang ditampilkan dalam tabel berikut ini umumnya digunakan dalam Blazor aplikasi.
Layanan | Seumur hidup | Deskripsi |
---|---|---|
HttpClient | Cakupan | Menyediakan metode untuk mengirim permintaan HTTP dan menerima respons HTTP dari sumber daya yang diidentifikasi oleh URI. Sisi klien, instans HttpClient didaftarkan oleh aplikasi dalam Sisi server, HttpClient tidak dikonfigurasi sebagai layanan secara default. Dalam kode sisi server, berikan HttpClient. Untuk informasi selengkapnya, lihat Memanggil API web dari aplikasi ASP.NET CoreBlazor. HttpClient Terdaftar sebagai layanan terlingkup, bukan singleton. Untuk informasi selengkapnya, lihat bagian Masa pakai layanan. |
IJSRuntime | Sisi klien: Singleton Sisi server: Terlingkup Blazor Kerangka kerja mendaftar IJSRuntime di kontainer layanan aplikasi. |
Mewakili instans runtime JavaScript tempat panggilan JavaScript dikirim. Untuk informasi lebih lanjut, lihat Memanggil fungsi JavaScript dari metode .NET di Blazor ASP.NET Core. Saat mencoba menyuntikkan layanan ke layanan singleton di server, lakukan salah satu pendekatan berikut:
|
NavigationManager | Sisi klien: Singleton Sisi server: Terlingkup Blazor Kerangka kerja mendaftar NavigationManager di kontainer layanan aplikasi. |
Berisi pembantu untuk bekerja dengan URI dan status navigasi. Untuk informasi selengkapnya, lihat URI dan pembantu status navigasi. |
Layanan tambahan yang terdaftar oleh Blazor kerangka kerja dijelaskan dalam dokumentasi tempat layanan tersebut digunakan untuk menjelaskan Blazor fitur, seperti konfigurasi dan pengelogan.
Penyedia layanan kustom tidak secara otomatis menyediakan layanan default yang tercantum dalam tabel. Jika Anda menggunakan penyedia layanan kustom dan memerlukan salah satu layanan yang ditampilkan dalam tabel, tambahkan layanan yang diperlukan ke penyedia layanan baru.
Menambahkan layanan sisi klien
Konfigurasikan layanan untuk kumpulan layanan aplikasi dalam Program
file. Dalam contoh berikut, ExampleDependency
implementasi terdaftar untuk IExampleDependency
:
var builder = WebAssemblyHostBuilder.CreateDefault(args);
...
builder.Services.AddSingleton<IExampleDependency, ExampleDependency>();
...
await builder.Build().RunAsync();
Setelah host dibuat, layanan tersedia dari cakupan DI akar sebelum komponen apa pun dirender. Ini dapat berguna untuk menjalankan logika inisialisasi sebelum merender konten:
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();
Host menyediakan instans konfigurasi pusat untuk aplikasi. Dibangun pada contoh sebelumnya, URL layanan cuaca diteruskan dari sumber konfigurasi default (misalnya, appsettings.json
) ke 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();
Menambahkan layanan sisi server
Setelah membuat aplikasi baru, periksa bagian dari Program
file:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();
Variabel builder
mewakili WebApplicationBuilder dengan IServiceCollection, yang merupakan daftar objek deskriptor layanan. Layanan ditambahkan dengan menyediakan deskriptor layanan ke koleksi layanan. Contoh berikut menunjukkan konsep dengan IDataAccess
antarmuka dan implementasi DataAccess
konkretnya :
builder.Services.AddSingleton<IDataAccess, DataAccess>();
Setelah membuat aplikasi baru, periksa Startup.ConfigureServices
metode di Startup.cs
:
using Microsoft.Extensions.DependencyInjection;
...
public void ConfigureServices(IServiceCollection services)
{
...
}
Metode ConfigureServices ini diteruskan IServiceCollection, yang merupakan daftar objek deskriptor layanan. Layanan ditambahkan dalam ConfigureServices
metode dengan memberikan deskriptor layanan ke kumpulan layanan. Contoh berikut menunjukkan konsep dengan IDataAccess
antarmuka dan implementasi DataAccess
konkretnya :
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IDataAccess, DataAccess>();
}
Mendaftarkan layanan umum
Jika satu atau beberapa layanan umum diperlukan klien dan sisi server, Anda dapat menempatkan pendaftaran layanan umum di sisi klien metode dan memanggil metode untuk mendaftarkan layanan di kedua proyek.
Pertama, faktor pendaftaran layanan umum ke dalam metode terpisah. Misalnya, buat ConfigureCommonServices
metode sisi klien:
public static void ConfigureCommonServices(IServiceCollection services)
{
services.Add...;
}
Untuk file sisi Program
klien, panggil ConfigureCommonServices
untuk mendaftarkan layanan umum:
var builder = WebAssemblyHostBuilder.CreateDefault(args);
...
ConfigureCommonServices(builder.Services);
Dalam file sisi Program
server, panggil ConfigureCommonServices
untuk mendaftarkan layanan umum:
var builder = WebApplication.CreateBuilder(args);
...
Client.Program.ConfigureCommonServices(builder.Services);
Untuk contoh pendekatan ini, lihat skenario keamanan tambahan ASP.NET CoreBlazor WebAssembly.
Layanan sisi klien yang gagal selama pra-penyajian
Bagian ini hanya berlaku untuk komponen WebAssembly dalam Blazor Web Apps.
Blazor Web Apps biasanya merender komponen WebAssembly sisi klien. Jika aplikasi dijalankan dengan layanan yang diperlukan hanya terdaftar dalam .Client
proyek, menjalankan aplikasi menghasilkan kesalahan runtime yang mirip dengan yang berikut ini ketika komponen mencoba menggunakan layanan yang diperlukan selama pra-penyajian:
InvalidOperationException: Tidak dapat memberikan nilai untuk {PROPERTY} pada tipe '{ASSEMBLY}}. Client.Pages. {COMPONENT NAME}'. Tidak ada layanan terdaftar tipe '{SERVICE}'.
Gunakan salah satu pendekatan berikut untuk mengatasi masalah ini:
- Daftarkan layanan di proyek utama untuk membuatnya tersedia selama pra-penyajian komponen.
- Jika pra-penyajian tidak diperlukan untuk komponen, nonaktifkan pra-penyajian dengan mengikuti panduan dalam mode render inti Blazor ASP.NET. Jika Anda mengadopsi pendekatan ini, Anda tidak perlu mendaftarkan layanan di proyek utama.
Untuk informasi selengkapnya, lihat Layanan sisi klien gagal diselesaikan selama pra-penyajian.
Masa pakai layanan
Layanan dapat dikonfigurasi dengan masa pakai yang diperlihatkan dalam tabel berikut.
Seumur hidup | Deskripsi |
---|---|
Scoped | Sisi klien saat ini tidak memiliki konsep cakupan DI. Pengembangan sisi server mendukung
Untuk informasi selengkapnya tentang mempertahankan status pengguna di aplikasi sisi server, lihat manajemen status ASP.NET CoreBlazor. |
Singleton | DI membuat satu instans layanan. Semua komponen yang Singleton memerlukan layanan menerima instans layanan yang sama. |
Transient | Setiap kali komponen mendapatkan instans Transient layanan dari kontainer layanan, komponen menerima instans layanan baru. |
Sistem DI didasarkan pada sistem DI di ASP.NET Core. Untuk informasi lebih lanjut, lihat Injeksi dependensi di ASP.NET Core.
Meminta layanan dalam komponen
Untuk menyuntikkan layanan ke dalam komponen, Blazor mendukung injeksi konstruktor dan injeksi properti.
Injeksi konstruktor
Setelah layanan ditambahkan ke kumpulan layanan, masukkan satu atau beberapa layanan ke dalam komponen dengan injeksi konstruktor. Contoh berikut menyuntikkan NavigationManager
layanan.
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");
}
}
Injeksi properti
Setelah layanan ditambahkan ke koleksi layanan, masukkan satu atau beberapa layanan ke dalam komponen dengan direktif @inject
Razor , yang memiliki dua parameter:
- Jenis: Jenis layanan yang akan disuntikkan.
- Properti: Nama properti yang menerima layanan aplikasi yang disuntikkan. Properti tidak memerlukan pembuatan manual. Pengkompilasi membuat properti .
Untuk informasi selengkapnya, lihat Injeksi dependensi ke dalam tampilan di ASP.NET Core.
Gunakan beberapa @inject
pernyataan untuk menyuntikkan layanan yang berbeda.
Contoh berikut menunjukkan cara menggunakan direktif @inject
. Penerapan layanan Services.NavigationManager
disuntikkan ke properti Navigation
komponen . Perhatikan bagaimana kode hanya menggunakan NavigationManager
abstraksi.
PropertyInjection.razor
:
@page "/property-injection"
@inject NavigationManager Navigation
<button @onclick="@(() => Navigation.NavigateTo("/counter"))">
Take me to the Counter component
</button>
Secara internal, properti yang dihasilkan (Navigation
) menggunakan [Inject]
atribut . Biasanya, atribut ini tidak digunakan secara langsung. Jika kelas dasar diperlukan untuk komponen dan properti yang disuntikkan juga diperlukan untuk kelas dasar, tambahkan [Inject]
atribut secara manual:
using Microsoft.AspNetCore.Components;
public class ComponentBase : IComponent
{
[Inject]
protected NavigationManager Navigation { get; set; } = default!;
...
}
Catatan
Karena layanan yang disuntikkan diharapkan tersedia, literal default dengan operator pengampunan null (default!
) ditetapkan di .NET 6 atau yang lebih baru. Untuk informasi selengkapnya, lihat Jenis referensi nullable (NRTs) dan .NET compiler null-state static analysis.
Dalam komponen yang berasal dari kelas dasar, @inject
direktif tidak diperlukan. Kelas InjectAttribute dasar sudah cukup. Komponen hanya memerlukan direktif @inherits
. Dalam contoh berikut, setiap layanan CustomComponentBase
yang disuntikkan tersedia untuk Demo
komponen:
@page "/demo"
@inherits CustomComponentBase
Menggunakan DI dalam layanan
Layanan kompleks mungkin memerlukan layanan tambahan. Dalam contoh berikut, DataAccess
memerlukan HttpClient layanan default. @inject
(atau [Inject]
atribut) tidak tersedia untuk digunakan dalam layanan. Injeksi konstruktor harus digunakan sebagai gantinya. Layanan yang diperlukan ditambahkan dengan menambahkan parameter ke konstruktor layanan. Ketika DI membuat layanan, LAYANAN mengenali layanan yang diperlukan dalam konstruktor dan menyediakannya dengan sesuai. Dalam contoh berikut, konstruktor menerima HttpClient melalui DI. HttpClient adalah layanan default.
using System.Net.Http;
public class DataAccess : IDataAccess
{
public DataAccess(HttpClient http)
{
...
}
...
}
Injeksi konstruktor didukung dengan konstruktor utama di C# 12 (.NET 8) atau yang lebih baru:
using System.Net.Http;
public class DataAccess(HttpClient http) : IDataAccess
{
...
}
Prasyarat untuk injeksi konstruktor:
- Satu konstruktor harus ada yang argumennya semuanya dapat dipenuhi oleh DI. Parameter tambahan yang tidak tercakup oleh DI diizinkan jika menentukan nilai default.
- Konstruktor yang berlaku harus
public
. - Satu konstruktor yang berlaku harus ada. Dalam kasus ambiguitas, DI melempar pengecualian.
Menyuntikkan layanan kunci ke dalam komponen
Blazor mendukung menyuntikkan layanan kunci menggunakan [Inject]
atribut . Kunci memungkinkan pencakupan pendaftaran dan konsumsi layanan saat menggunakan injeksi dependensi. InjectAttribute.Key Gunakan properti untuk menentukan kunci layanan yang akan disuntikkan:
[Inject(Key = "my-service")]
public IMyService MyService { get; set; }
Kelas komponen dasar utilitas untuk mengelola cakupan DI
Di aplikasi Non-ASP.NETBlazor Core, layanan terlingkup dan sementara biasanya dilingkup ke permintaan saat ini. Setelah permintaan selesai, layanan tercakup dan sementara dibuang oleh sistem DI.
Dalam aplikasi sisi Blazor server interaktif, cakupan DI berlangsung selama durasi sirkuit ( SignalR koneksi antara klien dan server), yang dapat mengakibatkan layanan sementara yang tercakup dan sekali pakai hidup lebih lama dari masa pakai satu komponen. Oleh karena itu, jangan langsung menyuntikkan layanan terlingkup ke dalam komponen jika Anda berniat seumur hidup layanan agar sesuai dengan masa pakai komponen. Layanan sementara yang disuntikkan ke dalam komponen yang tidak diterapkan IDisposable adalah sampah yang dikumpulkan ketika komponen dibuang. Namun, layanan sementara yang disuntikkan yang diterapkan IDisposable dipertahankan oleh kontainer DI selama masa pakai sirkuit, yang mencegah pengumpulan sampah layanan ketika komponen dibuang dan menghasilkan kebocoran memori. Pendekatan alternatif untuk layanan tercakup berdasarkan OwningComponentBase jenisnya dijelaskan nanti di bagian ini, dan layanan sementara sekali pakai tidak boleh digunakan sama sekali. Untuk informasi selengkapnya, lihat Desain untuk memecahkan sekali pakai sementara pada Blazor Server (dotnet/aspnetcore
#26676).
Bahkan di aplikasi sisi Blazor klien yang tidak beroperasi melalui sirkuit, layanan yang terdaftar dengan masa pakai cakupan diperlakukan sebagai singleton, sehingga mereka hidup lebih lama dari layanan terlingkup di aplikasi ASP.NET Core biasa. Layanan sementara sekali pakai sisi klien juga hidup lebih lama dari komponen tempat mereka disuntikkan karena kontainer DI, yang menyimpan referensi ke layanan sekali pakai, bertahan selama masa pakai aplikasi, mencegah pengumpulan sampah pada layanan. Meskipun layanan sementara sekali pakai berumur panjang menjadi perhatian yang lebih besar di server, layanan tersebut juga harus dihindari sebagai pendaftaran layanan klien. Penggunaan OwningComponentBase jenis ini juga direkomendasikan untuk layanan tercakup sisi klien untuk mengontrol masa pakai layanan, dan layanan sementara sekali pakai tidak boleh digunakan sama sekali.
Pendekatan yang membatasi masa pakai layanan adalah penggunaan jenis tersebut OwningComponentBase . OwningComponentBase adalah jenis abstrak yang berasal dari ComponentBase yang membuat cakupan DI yang sesuai dengan masa pakai komponen. Dengan menggunakan cakupan ini, komponen dapat menyuntikkan layanan dengan masa pakai terlingkup dan membuatnya hidup selama komponen. Ketika komponen dihancurkan, layanan dari penyedia layanan tercakup komponen juga dibuang. Ini dapat berguna untuk layanan yang digunakan kembali dalam komponen tetapi tidak dibagikan di seluruh komponen.
Dua versi jenis OwningComponentBase tersedia dan dijelaskan di dua bagian berikutnya:
OwningComponentBase
OwningComponentBaseadalah anak abstrak dan sekali pakai dari ComponentBase jenis dengan properti jenis IServiceProvideryang dilindungi ScopedServices . Penyedia dapat digunakan untuk menyelesaikan layanan yang terlingkup hingga masa pakai komponen.
Layanan DI yang disuntikkan ke dalam komponen menggunakan @inject
atau [Inject]
atribut tidak dibuat dalam cakupan komponen. Untuk menggunakan cakupan komponen, layanan harus diselesaikan menggunakan ScopedServices dengan GetRequiredService atau GetService. Setiap layanan yang diselesaikan menggunakan ScopedServices penyedia memiliki dependensi yang disediakan dalam cakupan komponen.
Contoh berikut menunjukkan perbedaan antara menyuntikkan layanan tercakup secara langsung dan menyelesaikan layanan menggunakan ScopedServices di server. Antarmuka dan implementasi berikut untuk kelas perjalanan waktu mencakup DT
properti untuk menyimpan DateTime nilai. Implementasi memanggil DateTime.Now untuk mengatur DT
kapan TimeTravel
kelas dibuat.
ITimeTravel.cs
:
public interface ITimeTravel
{
public DateTime DT { get; set; }
}
TimeTravel.cs
:
public class TimeTravel : ITimeTravel
{
public DateTime DT { get; set; } = DateTime.Now;
}
Layanan ini terdaftar sebagai cakupan dalam file sisi Program
server. Layanan tercakup sisi server memiliki masa pakai yang sama dengan durasi sirkuit.
Dalam file Program
:
builder.Services.AddScoped<ITimeTravel, TimeTravel>();
Dalam komponen TimeTravel
berikut:
- Layanan perjalanan waktu langsung disuntikkan dengan
@inject
sebagaiTimeTravel1
. - Layanan ini juga diselesaikan secara terpisah dengan ScopedServices dan GetRequiredService sebagai
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>();
}
}
Awalnya menavigasi ke TimeTravel
komponen, layanan perjalanan waktu dibuat dua kali ketika komponen dimuat, dan TimeTravel1
dan TimeTravel2
memiliki nilai awal yang sama:
TimeTravel1.DT: 8/31/2022 2:54:45 PM
TimeTravel2.DT: 8/31/2022 2:54:45 PM
Saat menavigasi menjauh dari komponen ke TimeTravel
komponen lain dan kembali ke TimeTravel
komponen:
TimeTravel1
disediakan instans layanan yang sama yang dibuat ketika komponen pertama kali dimuat, sehingga nilainyaDT
tetap sama.TimeTravel2
mendapatkan instans layanan baruITimeTravel
denganTimeTravel2
nilai DT baru.
TimeTravel1.DT: 8/31/2022 2:54:45 PM
TimeTravel2.DT: 8/31/2022 2:54:48 PM
TimeTravel1
terkait dengan sirkuit pengguna, yang tetap utuh dan tidak dibuang sampai sirkuit yang mendasar didekonstruksi. Misalnya, layanan dibuang jika sirkuit terputus untuk periode retensi sirkuit yang terputus.
Terlepas dari pendaftaran layanan terlingkup dalam Program
file dan umur panjang sirkuit pengguna, TimeTravel2
menerima instans layanan baru ITimeTravel
setiap kali komponen diinisialisasi.
OwningComponentBase<TService>
OwningComponentBase<TService> berasal dari OwningComponentBase dan menambahkan Service properti yang mengembalikan instans dari T
penyedia DI tercakup. Jenis ini adalah cara mudah untuk mengakses layanan tercakup tanpa menggunakan instans IServiceProvider ketika ada satu layanan utama yang diperlukan aplikasi dari kontainer DI menggunakan cakupan komponen. Properti ScopedServices tersedia, sehingga aplikasi bisa mendapatkan layanan dari jenis lain, jika perlu.
@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>
Mendeteksi sekali pakai sementara sisi klien
Kode kustom dapat ditambahkan ke aplikasi sisi Blazor klien untuk mendeteksi layanan sementara sekali pakai di aplikasi yang harus menggunakan OwningComponentBase. Pendekatan ini berguna jika Anda khawatir kode yang ditambahkan ke aplikasi di masa mendatang menggunakan satu atau beberapa layanan sekali pakai sementara, termasuk layanan yang ditambahkan oleh pustaka. Kode demonstrasi tersedia di Blazor repositori GitHub sampel (cara mengunduh).
Periksa yang berikut ini dalam versi .NET 6 atau yang lebih baru BlazorSample_WebAssembly
dari sampel:
DetectIncorrectUsagesOfTransientDisposables.cs
Services/TransientDisposableService.cs
- Dalam:
Program.cs
- Namespace layanan aplikasi
Services
disediakan di bagian atas file (using BlazorSample.Services;
). DetectIncorrectUsageOfTransients
dipanggil segera setelahbuilder
ditetapkan dari WebAssemblyHostBuilder.CreateDefault.TransientDisposableService
terdaftar (builder.Services.AddTransient<TransientDisposableService>();
).EnableTransientDisposableDetection
dipanggil pada host bawaan dalam alur pemrosesan aplikasi (host.EnableTransientDisposableDetection();
).
- Namespace layanan aplikasi
- Aplikasi ini mendaftarkan
TransientDisposableService
layanan tanpa melemparkan pengecualian. Namun, mencoba menyelesaikan layanan dalamTransientService.razor
melempar InvalidOperationException ketika kerangka kerja mencoba membangun instansTransientDisposableService
.
Mendeteksi sekali pakai sementara sisi server
Kode kustom dapat ditambahkan ke aplikasi sisi Blazor server untuk mendeteksi layanan sementara sekali pakai sisi server di aplikasi yang harus menggunakan OwningComponentBase. Pendekatan ini berguna jika Anda khawatir kode yang ditambahkan ke aplikasi di masa mendatang menggunakan satu atau beberapa layanan sekali pakai sementara, termasuk layanan yang ditambahkan oleh pustaka. Kode demonstrasi tersedia di Blazor repositori GitHub sampel (cara mengunduh).
Periksa yang berikut ini di .NET 8 atau versi sampel yang lebih baru BlazorSample_BlazorWebApp
:
Periksa contoh berikut dalam versi BlazorSample_Server
.NET 6 atau .NET 7:
DetectIncorrectUsagesOfTransientDisposables.cs
Services/TransitiveTransientDisposableDependency.cs
:- Dalam:
Program.cs
- Namespace layanan aplikasi
Services
disediakan di bagian atas file (using BlazorSample.Services;
). DetectIncorrectUsageOfTransients
dipanggil pada pembangun host (builder.DetectIncorrectUsageOfTransients();
).- Layanan
TransientDependency
terdaftar (builder.Services.AddTransient<TransientDependency>();
). TransitiveTransientDisposableDependency
terdaftar untukITransitiveTransientDisposableDependency
(builder.Services.AddTransient<ITransitiveTransientDisposableDependency, TransitiveTransientDisposableDependency>();
).
- Namespace layanan aplikasi
- Aplikasi ini mendaftarkan
TransientDependency
layanan tanpa melemparkan pengecualian. Namun, mencoba menyelesaikan layanan dalamTransientService.razor
melempar InvalidOperationException ketika kerangka kerja mencoba membangun instansTransientDependency
.
Pendaftaran layanan sementara untuk IHttpClientFactory
/HttpClient
handler
Pendaftaran layanan sementara untuk IHttpClientFactory/HttpClient handler disarankan. Jika aplikasi berisi IHttpClientFactory/HttpClient handler dan menggunakan IRemoteAuthenticationBuilder<TRemoteAuthenticationState,TAccount> untuk menambahkan dukungan untuk autentikasi, penggunaan sementara berikut untuk autentikasi sisi klien juga ditemukan, yang diharapkan dan dapat diabaikan:
Instans IHttpClientFactory/HttpClient lain juga ditemukan. Instans ini juga dapat diabaikan.
Aplikasi Blazor sampel di Blazor repositori GitHub sampel (cara mengunduh) menunjukkan kode untuk mendeteksi sekali pakai sementara. Namun, kode dinonaktifkan karena aplikasi sampel menyertakan IHttpClientFactory/HttpClient handler.
Untuk mengaktifkan kode demonstrasi dan menyaksikan operasinya:
Batalkan komentar baris sekali pakai sementara di
Program.cs
.Hapus cek masuk
NavLink.razor
kondisi yang mencegahTransientService
komponen ditampilkan di bilah sisi navigasi aplikasi:- else if (name != "TransientService") + else
Jalankan aplikasi sampel dan navigasikan ke
TransientService
komponen di/transient-service
.
Penggunaan Entity Framework Core (EF Core) DbContext dari DI
Untuk informasi selengkapnya, lihat ASP.NET Core Blazor dengan Entity Framework Core (EF Core).
Mengakses layanan sisi Blazor server dari cakupan DI yang berbeda
Penanganan aktivitas sirkuit menyediakan pendekatan untuk mengakses layanan tercakup Blazor dari cakupan injeksi non-dependensiBlazor (DI) lainnya, seperti cakupan yang dibuat menggunakan IHttpClientFactory.
Sebelum rilis ASP.NET Core di .NET 8, mengakses layanan cakupan sirkuit dari cakupan injeksi dependensi lain yang diperlukan menggunakan jenis komponen dasar kustom. Dengan penanganan aktivitas sirkuit, jenis komponen dasar kustom tidak diperlukan, seperti yang ditunjukkan contoh berikut:
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;
}
}
Akses layanan cakupan sirkuit dengan menyuntikkan tempat CircuitServicesAccessor
yang diperlukan.
Untuk contoh yang memperlihatkan cara mengakses AuthenticationStateProvider dari DelegatingHandler penyiapan menggunakan IHttpClientFactory, lihat Skenario keamanan tambahan sisi server ASP.NET CoreBlazor.
Mungkin ada kalanya Razor komponen memanggil metode asinkron yang menjalankan kode dalam cakupan DI yang berbeda. Tanpa pendekatan yang benar, cakupan DI ini tidak memiliki akses ke Blazorlayanan 's, seperti IJSRuntime dan Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage.
Misalnya, HttpClient instans yang dibuat menggunakan IHttpClientFactory memiliki cakupan layanan DI mereka sendiri. Akibatnya, HttpMessageHandler instans yang dikonfigurasi pada HttpClient tidak dapat langsung menyuntikkan Blazor layanan.
Buat kelas BlazorServiceAccessor
yang mendefinisikan AsyncLocal
, yang menyimpan BlazorIServiceProvider untuk konteks asinkron saat ini. BlazorServiceAccessor
Instans dapat diperoleh dari dalam cakupan layanan DI yang berbeda untuk mengakses Blazor layanan.
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; }
}
}
Untuk mengatur nilai BlazorServiceAccessor.Services
secara otomatis saat async
metode komponen dipanggil, buat komponen dasar kustom yang mengimplementasikan ulang tiga titik entri asinkron utama ke dalam Razor kode komponen:
Kelas berikut menunjukkan implementasi untuk komponen dasar.
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;
}
}
}
Komponen apa pun yang diperluas CustomComponentBase
secara otomatis telah BlazorServiceAccessor.Services
diatur ke IServiceProvider dalam cakupan DI saat ini Blazor .
Terakhir, dalam Program
file, tambahkan BlazorServiceAccessor
sebagai layanan terlingkup:
builder.Services.AddScoped<BlazorServiceAccessor>();
Akhirnya, di Startup.ConfigureServices
dari Startup.cs
, tambahkan BlazorServiceAccessor
sebagai layanan terlingkup:
services.AddScoped<BlazorServiceAccessor>();
Sumber Daya Tambahan:
ASP.NET Core