選單
GSS 技術部落格
在這個園地裡我們將從技術、專案管理、客戶對談面和大家分享我們多年的經驗,希望大家不管是喜歡或是有意見,都可以回饋給我們,讓我們有機會和大家對話並一起成長!
若有任何問題請來信:gss_crm@gss.com.tw
3 分鐘閱讀時間 (561 個字)

[Dot Net Core](Graphic series )10. The easiest way to verify the feature of Resolved Singleton objects in the architecture

netcorelogo

 在上一節,透過描述我們看到controller class在被產生instance過程中,其中IActionInvokerFactory 會被Resolve成ActionInvokerFactory實體。這時候建構子內的參數物件也會繼續透過DI機制被Resolve成實體。

而在"UseEndpoint to Map Controller"這節中,實際上ApplicationPartManager此instance的產出是因為它的身分是ControllerActionDescriptorProvider的建構子參數物件。在被Resolve 出來時,ApplicationPartManager 的ApplicationParts 集合已經包含 Controller Class 所屬的組件資訊。

回顧下圖在"UseEndpoint to Map Controller"這節中的圖: 

但是令人好奇的是,建構子內並沒有去新增ApplicationPartManager 的ApplicationParts 集合內容的程式碼,但為何剛開始被建構出來的這個物件此集合內已經有值了?

原因是在這個時間點之前就已經被Resolve 過了,Resolve 的形式是 Singleton;且透過ApplicationPartManager.PopulateDefaultParts將組件資訊包裝到ApplicationPartManager.ApplicationParts 集合,供後續再進一步取出Controller與Action 資訊。在確切的時間點會在下一節做描述。

我們可以在Dot Net Core架構中做一個簡單的Resolve Singleton 物件,證明其記憶體位置與內容是可以被保存到下一次被 Resolve 出來(仍為Singleton的實體)。

有幾個步驟:

Step1: 建立簡單的類別TestClass ,如下: 

public class TestClass
{

    public List<string> ListStringCollection;

    public TestClass()
    {
        if (ListStringCollection is null)
            ListStringCollection = new List<string>();
    }

    public void AddString(string strText)
    {
        ListStringCollection?.Add(strText);
    }

} 

Step2: 於Startup 類別新增一個static欄位IApplicationBuilder AppBuilder,於ConfigureServices函式中註冊此類別;於Configure函式中 Resolve TestClass,並於TestClass. ListStringCollection字串集合加入一個日期字串。然後把IapplicationBuilder的實體指派到 Startup. AppBuilder。 如下:

public class Startup
    {

        public static IApplicationBuilder AppBuilder;   


        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.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

            services.AddSingleton<TestClass>();
        }

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

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });


            //Demo Singleton
            TestClass ts = (TestClass)app.ApplicationServices.GetService(typeof(TestClass));
            ts.ListStringCollection.Add(DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
            System.Diagnostics.Debug.Write( $" The class memory location is  {AddressHelper.GetAddress(ts)}" );
            System.Diagnostics.Debug.Write($"The first element in collection is : {ts.ListStringCollection[0]}  ");

            //cache IApplicationBuilder instance
            AppBuilder = app;
        }
    } 

Step3: 執行專案,並觀察Startup. Configure中,利用AppBuilder的serviceProviderEngineScope來做IOC的Resolve Service,所被Resolve 出來的 TestClass 記憶體位置與其成員內容。

Step4: Request to Controller Action (Default Controller "WeatherForecastController")

Step5: 觀察執行到Controller的Action 時,再次Resolve出的TestClass 是否記憶體位置與ListStringCollection字串集合所存的字串內容是否與之前一致。

如上圖,最後Resolve 出 TestClass時,都未再對其成員內容做任何變動,建構子也是如第一張圖一樣沒有更動成員的內容;而記憶體位置與ListStringCollection字串集合所存的字串內容都與在 Startup. Configure中所指定的時間字串是一致的。

在Startup. ConfigureServices中,如果是register service 前就先產生實體且設定內容,也可以有一樣的結果。方式如下:

public class Startup
{

    public static IApplicationBuilder AppBuilder;   


    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.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();

        // original 
        //services.AddSingleton<TestClass>();
        
        // new instance then register service 
        TestClass ts = new TestClass();
        ts.ListStringCollection.Add(DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
        System.Diagnostics.Debug.Write($" The class memory location is  {AddressHelper.GetAddress(ts)} \n ");
        System.Diagnostics.Debug.Write($"The first element in collection is : {ts.ListStringCollection[0]}  ");
        services.AddSingleton(ts);
        
    }

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

        app.UseHttpsRedirection();

        app.UseRouting();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });


        //Demo Singleton
        TestClass ts = (TestClass)app.ApplicationServices.GetService(typeof(TestClass));
        System.Diagnostics.Debug.Write( $" The class memory location is  {AddressHelper.GetAddress(ts)} \n " );
        System.Diagnostics.Debug.Write($"The first element in collection is : {ts.ListStringCollection[0]}  ");

        //cache IApplicationBuilder instance
        AppBuilder = app;
    }
} 

所以透過上述證明,Singleton的實體一旦被Resolve出來,記憶體位置都會保留起來。同理可以說明,當處理Resolve Controller Class時 ,ApplicationPartManager 這個Class,在執行endpoint middleware階段被Resolve出來時已經包含Controller所在的組件資訊。下一節會再詳細說明。

[Dot Net Core](Graphic series ) 11. Explain at wha...
[Dot Net Core](Graphic series)9. Comparison of Dot...
 

評論

尚無評論
已經注冊了? 這裡登入
Guest
2024/04/23, 週二

Captcha 圖像