2019年12月10日 星期二

ASP.NET Core Blazor 在元件 Component 上的注入各種服務的存留期研究

ASP.NET Core Blazor 在元件 Component 上的注入各種服務的存留期研究

更多關於 Blazor 教學影片,可以參考 Blazor 教學影片播放清單 或者 Blazor 快速體驗教學影片撥放清單。也歡迎訂閱本 .NET / Blazor / Xamarin.Forms 影片頻道 。


當在使用 Blazor 進行專案開發的時候,可以使用 ASP.NET Core 提供的相依性注入服務功能,此時僅需要宣告與建立所需要的抽象介面與具體實作類別,接著在 Startup 類別內的 ConfigureServices 方法內,進行註冊這些抽象介面與類別到相依性入 DI Container 容器內;當想要使用這些服務的時候,便可以在所建立的元件 Component 內,使用 @inject 語法便可以將這些服務注入到該元件內來使用。
在這篇文章所提到的專案原始碼,可以從 GitHub 下載
底下將會是關於抽象型別與具體實作類別和 Startup 類別的程式碼
namespace BlazorScopedSingleton
{
    public interface IMessageTransient
    {
        string Hash();
    }
    public interface IMessageScoped
    {
        string Hash();
    }
    public interface IMessageSingleton
    {
        string Hash();
    }
    public class MessageTransient : IMessageTransient
    {
        public string Hash()
        {
            return $"MessageTransient : {this.GetHashCode()}";
        }
    }
    public class MessageScoped : IMessageScoped
    {
        public string Hash()
        {
            return $"MessageScoped : {this.GetHashCode()}";
        }
    }
    public class MessageSingleton : IMessageSingleton
    {
        public string Hash()
        {
            return $"MessageSingleton : {this.GetHashCode()}";
        }
    }
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddRazorPages();
            services.AddServerSideBlazor();
            services.AddSingleton<WeatherForecastService>();

            services.AddTransient<IMessageTransient, MessageTransient>();
            services.AddScoped<IMessageScoped, MessageScoped>();
            services.AddSingleton<IMessageSingleton, MessageSingleton>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapBlazorHub();
                endpoints.MapFallbackToPage("/_Host");
            });
        }
    }
}
在上面的程式碼中,將會建立三個介面並且使用這三個介面分別實作三個具體實作類別,從這些介面與類別名稱,可以充分表達是要進行 DI 存留期 Transient , Scoped , Singleton 的測試,了解到這些相依性注入的存留期在 Blazor 中表現行為為何。
這裡所建立的 Blazor 專案為 Serivce 端的 Blazor 專案。
首先,將會建立一個名為 ForContainer 的 Component 元件,其程式碼如下所示
@page "/ForContainer"
@inject IMessageTransient messageTransient1
@inject IMessageTransient messageTransient2
@inject IMessageScoped messageScoped1
@inject IMessageScoped messageScoped2
@inject IMessageSingleton messageSingleton1
@inject IMessageSingleton messageSingleton2

<h3>ForContainer</h3>

<div class="bg-info">messageTransient1 的物件為 @messageTransient1.Hash()</div>
<div class="bg-warning">messageTransient2 的物件為 @messageTransient2.Hash()</div>
<div class="bg-info">messageScoped1 的物件為 @messageScoped1.Hash()</div>
<div class="bg-warning">messageScoped1 的物件為 @messageScoped2.Hash()</div>
<div class="bg-info">messageSingleton1 的物件為 @messageSingleton1.Hash()</div>
<div class="bg-warning">messageSingleton2 的物件為 @messageSingleton2.Hash()</div>

@code {

}
在這裡,將會使用 @inject 語法,將不同介面所需要的服務,注入到這個元件中,其中,每個介面都會注入兩次到不同的變數內,以便觀察有何變化。
現在可以執行專案,並且切換網址列為 https://localhost:5001/ForContainer
底下為第一次執行的結果,從這裡可以看的出來,當使用 Transient 存留期註冊的服務,只要每次注入這個服務的時候,就會得到不同的物件,因為 DI Container 容器會立即產生出的新的執行個體出來;當使用 Scoped 存留期,因為是在同一個 Connection ,所以,會得到同一個執行個體物件,同樣的,Singleton 存留期,在這次執行過程中,也是得到相同的物件。
現在,在瀏覽器上開啟一個新的標籤頁次,輸入同樣的網址,看看執行結果
當然,對於 Transient 存留期,同樣的會產生兩個嶄新的物件,而對於 Scoped 的注入請求,因為在這裡是一個新的連線,因此,在這次新的連線過程中,多次注入同一個介面,會得到同一個物件,這點也是沒有問題;而對於 Singleton 存留期,因為這個專案還在執行中,所以,將會得到與上面同樣的物件,沒有任何變化。
此時,再來產生一個新的元件,在這個元件中,將會使用剛剛設計的 ForContainer 元件兩次,現在,來看看這樣會有甚麼執行結果。
@page "/ForMultipleComponent"
@using BlazorScopedSingleton.Pages

<h3>ForMultipleComponent</h3>

<ForContainer />
<ForContainer />

@code {

}
這裡將會打開 https://localhost:5001/ForMultipleComponent 網址,執行結果如下面螢幕截圖
很清楚的,只要這個專案還在執行階段,只要使用 Singleton 方式來注入,不論是單一元件,還是開多個網頁甚至使用巢狀元件的方式,都會得到同一個物件,這裡的表現符合預期;而對 Transient 而言,也是一樣,只要每次注入的時候,都會得到一個新的物件;至於 Scoped 這樣的存留期,將過這裡的測試,得到只要是在同一個連現階段,不論是對於單一元件或者同樣的元件要顯示多次,都會在同一個連線過程中,得到同一個物件。
現在,再來重新整理這個網址 https://localhost:5001/ForMultipleComponent 網址,執行結果如下面螢幕截圖,這樣的結果也是符合預期的。




沒有留言:

張貼留言