前面說過自己建立的 Service 都必須在 Program.cs 註冊,但有些基本 Service 就不用自己做了。
目前 Blazor 提供內建的 Service 有三個,分別為:
HttpClient:處理 http 請求,生命週期為 Scoped(注意:只有 Blazor WebAssembly 有提供,Blazor Server 必須自己註冊)IJSRuntime:提供 JavaScript runtime 元件處理 JS 功能,Blazor WebAssembly 生命週期為Singleton,Blazor Server 生命週期為ScopedNavigationManager:處理路由導向和狀態,Blazor WebAssembly 生命週期為Singleton,Blazor Server 生命週期為Scoped
生命週期指的就是 Component 存活的時間,除了 Singleton 跟 Scoped,還有一種 Transient。
Singleton是指從程式啟動到結束都只會有一個實例,所有 Component 共用一個實例。Transient則是每次使用該 Component 時,都會產生一個新實例。Scoped較為特別,Blazor Server 跟 Blazor WebAssembly 模式不相同,Blazor Server 的Scoped是指每次 HTTP 請求都會產生一個新的實例,但 Component 之間透過 SingalR 傳遞不會產生,微軟文件說明「Blazor WebAssembly 目前沒有 DI 的概念,Scoped相當於Singleton」。
不過筆者當初看了上述說明也是很懵,直到看了一個影片用 GUID 示範後才有所明白,我們就來試試看。
首先建立一個介面 IGuidService,裡面只有一個型別為 string 的屬性 UId,接著建立類別 GuidService 並在建構函式中初始化屬性 UId 為 GUID 字串,再去 Program.cs 使用 AddTransient 註冊。

然後建立一個 Guid.razor Component,裡面只有三行分別定義路由、注入服務以及顯示 GUID 字串,因為這案例很簡單所以沒用到 ComponentBase,所以需要在 _Import.razor 加入 @using BlazorServer.Services,最後為了切換方便,在 NavMenu.razor 定義一組 NavLink 指向剛才建立的 Guid.razor。

啟動後不論在 Post 及 Guid 頁面切換,或是重新載入頁面,都可以看到生成全新的一組 GUID,這就是 Transient 的特性:每次切換都產生新的實例。

接著將註冊方式改為 Singleton,可以看到就算重新載入網頁,也都是同一組 GUID,這就是 Singleton 的特性:程式啟動到結束都只會有一個實例。

最後將註冊方式改為 Scoped,切換到 Post 頁面再切回來,還是同一組 GUID,但重新載入頁面時就會產生新的一組,這就是 Scoped 的特性:每次產生 HTTP 請求都會有新的實例,Component 之間則不會產生新實例。

上述的例子是以 Blazor Server 進行,若以 Blazor WebAssembly 進行,則 Singleton 會產生跟 Blazor Server 不同的情況,原因就是 Blazor WebAssembly 沒有伺服器端,每次重新載入網頁都會將程式下載到瀏覽器,這是一個全新的 HTTP 請求,所以 Singleton 跟 Scoped 都是只要一重新載入網頁就會產生新的實例。


註:筆者為了方便省略影片中某些內容,有興趣的人可以再研究
引用:
- Blazor Course-Use ASP.NET Core to Build Full-Stack C# Web Apps
- ASP.NET Core Blazor dependency injection
註:本文程式碼透過 .NET 6 + Visual Studio 2022 重構,可點選原文連結與重構後程式碼比較學習,謝謝閱讀,支持原作者