2019年6月12日 星期三

在 .NET Core 下使用 DI 容器,對於實作類別的建構函式的參數不同數量測試比較

在 .NET Core 下使用 DI 容器,對於實作類別的建構函式的參數不同數量測試比較

在這篇文章中,將要來測試 .NET Core 下的 Microsoft.Extensions.DependencyInjection 套件,若要注入的具體實作物件中有多個多載建構函式存在的時候,會發生甚麼樣的問題呢?
首先,先來建立要測試的各種介面與具體實作類別,如底下的程式碼;在這裡將會宣告出四個介面,分別是 IMyInterface1, IMyInterface2, IMyInterface3, IYourInterface,前三者的具體實作類別將會是:MyClass1, MyClass2, MyClass3。
對於 IYourInterface 將會由類別 YourClass 來實作,在這個 YourClass 類別內,將會先建立 5 個建構函式,這些建構函式內都會有 Console.WriteLine 函式,輸出這個建構函式的特性文字。
  • 建構函式 1
    為預設建構函式
  • 建構函式 2
    將會需要提供 IMyInterface1, IMyInterface2 具體實作物件,共 2 個參數
  • 建構函式 3
    將會需要提供 IMyInterface1, IMyInterfac3 具體實作物件,共 2 個參數
  • 建構函式 4
    這個建構函式將需要 2 個參數,第一個為 IMyInterface1,第二個為 字串
  • 建構函式 5
    這個建構函式將需要 3 個參數,第一個為 IMyInterface1,第二個為 字串,第三個為 整數
C Sharp / C#
interface IMyInterface1 { }
class MyClass1 : IMyInterface1 { }
interface IMyInterface2 { }
class MyClass2 : IMyInterface2 { }
interface IMyInterface3 { }
class MyClass3 : IMyInterface3 { }
interface IYourInterface { }
class YourClass : IYourInterface
{
    // 建構函式 1
    public YourClass()
    {
        Console.WriteLine("YourClass 預設建構式被呼叫");
    }
    // 建構函式 2
    public YourClass(IMyInterface1 myInterface1, IMyInterface2 myInterface2)
    {
        Console.WriteLine("YourClass 建構式(IMyInterface1, IMyInterface2) 被呼叫");
    }
    // 建構函式 3
    public YourClass(IMyInterface1 myInterface1, IMyInterface3 myInterface3)
    {
        Console.WriteLine("YourClass 建構式(IMyInterface1, IMyInterface3) 被呼叫");
    }
    // 建構函式 4
    public YourClass(IMyInterface1 myInterface1, string myString)
    {
        Console.WriteLine("YourClass 建構式(IMyInterface1, string) 被呼叫");
    }
    // 建構函式 5
    public YourClass(IMyInterface1 myInterface1, string myString, int     myInt)
    {
        Console.WriteLine("YourClass 建構式5(IMyInterface1, string, int) 被呼叫");
    }
}
在這裡將會將會透過底下的程式碼,先透過 ServiceCollection 物件,使用 DI 容器 Container 來進行抽象型別與具體實作類別的註冊,這裡將會使用 AddTransient 方法來進行 暫時性 的執行個體存留期註冊 ( 每次從服務容器要求暫時性存留期服務時都會建立它們。 此存留期最適合用於輕量型的無狀態服務 ),接者使用 ServiceCollection.BuildServiceProvider() 方法取得 IServiceProvider ,如此,最後就可以透過 ServiceProvider.GetService() 方法來取得 IYourInterface 的實作物件。
C Sharp / C#
class Program
{
    static void Main(string[] args)
    {
        ServiceCollection services = new ServiceCollection();
        services.AddTransient<IMyInterface1, MyClass1>();
        services.AddTransient<IMyInterface2, MyClass2>();
        services.AddTransient<IMyInterface3, MyClass3>();
        services.AddTransient<IYourInterface, YourClass>();
        ServiceProvider serviceProvider = services.BuildServiceProvider();

        IYourInterface yourInterface = serviceProvider.GetService<IYourInterface>();

        Console.WriteLine("Press any key for continuing...");
        Console.ReadKey();
    }
}

第 1 次 .NET Core 相依性注入的測試

把上面的程式碼建立起來之後,將 YourClass 類別內的 建構函式4, 建構函式5 這兩個建構函式註解起來,接著將這個專案執行起來,不幸的是,該專案將無法正常執行起來,因為會拋出例外異常,從這個拋出例外異常訊息可以看出,因為該具體實作類別 YourClass 內,現在共有三個多載建構函式,原則上,當 .NET Core 相依性注入容器要注入一個物件的時候,會去搜尋該具體實作類別內的多載建構函式,並且將會選取建構函式參數數量最多的作為產生該物件之後的初始化方法。
可是, 建構函式 2 與 建構函式 3 同樣是需要 2 個參數,而且這兩個參數型別都是介面,這些介面也都有使用 ServiceCollection 註冊到 DI / IoC Container 容器 內,因此,DI 容器將無法決定要使用哪個 建構函式 來做物件的初始化,最後結果將會是無法完成注入這個物件過程,並且得到例外異常錯誤訊息。
System.InvalidOperationException: 'Unable to activate type 'DIConstructorParameters.YourClass'. The following constructors are ambiguous:
Void .ctor(DIConstructorParameters.IMyInterface1, DIConstructorParameters.IMyInterface2)

第 2 次 .NET Core 相依性注入的測試

現在請將 建構函式 3 註解起來 (這個時候,建構函式 4, 5 也被註解起來了),避免剛剛產生的錯誤訊息,再度執行一次,現在在螢幕上將會得到底下內容。因為此時該 YourClass 內僅有 建構函式 1 / 建構函式 2 ,所以,當要注入 YourClass 類別之後,將會執行 建構函式 2 來做該物件的初始化。
YourClass 建構式2(IMyInterface1, IMyInterface2) 被呼叫

第 3 次 .NET Core 相依性注入的測試

接下來來把 建構函式 5 註解起來,現在在 YourClass 類別內共有三個建構函式(1,2,4),其中建構函式參數數量最多的是 建構函式 2 / 建構函式 4 ,分別需要提供 兩個 參數,不過,對於 建構函式 2 所需要的兩個參數,都是介面型別,而 建構函式 4 則需要一個介面型別參數與一個字串常數。
現在可以執行這個專案,此時竟然沒有任何例外異常訊息拋出,而是在螢幕上輸出底下內容,從這個輸出文字可以看到 建構函式 2 被呼叫了,可是,建構函式 2 / 建構函式 4 都是有兩個參數,那為什麼不會產生錯誤,而是 建構函式 2 被呼叫了。這是因為 建構函式 4 的第二個參數為 字串 型別,這是屬於 .NET 開發框架下的基本型別,因此,這個型別將不會被列入選擇建構式的條件,所以, 建構函式 2 將會被選擇,而且這是唯一的 建構函式,不會有模稜兩可的問題。
YourClass 建構式2(IMyInterface1, IMyInterface2) 被呼叫

第 4 次 .NET Core 相依性注入的測試

現在可以把 建構函式 5 解除註解,現在在 YourClass 類別內共有四個建構函式(1,2,4,5),其中建構函式參數數量最多的是 建構函式 5 ,不過該建構函式的最後兩個參數的型別都是基本型別(string 與 int),所以,實際上僅有一個 參數。
因此,當執行這個專案,當然就會選擇建構函式 2。
YourClass 建構式2(IMyInterface1, IMyInterface2) 被呼叫



沒有留言:

張貼留言