ASP.NET Core 的相依性注入容器 Dependency Injection IoC Container 進行註冊同一個抽象介面多次的研究
- 同一個介面,註冊了不同的具體實作類別,會有甚麼情況?
- 當要進行解析的時候,究竟會取得哪個具體實作類別呢?
IServiceCollection
內,是會存在於多筆的同一個介面註冊資訊,還是會只有一個呢?
在這篇文章所提到的專案原始碼,可以從 GitHub 下載
測試程式碼說明
在這裡,將會建立一個空白的 ASP.NET Core 專案,並且在 Startup.cs 這個專案內,填入底下程式碼:
namespace MultiDIRegister
{
public interface IMessage
{
string Output(string msg);
}
public class ConsoleMessage : IMessage
{
public string Output(string msg)
{
return $"Console : {msg}</br>";
}
}
public class FileMessage : IMessage
{
public string Output(string msg)
{
return $"File : {msg}</br>";
}
}
public class Startup
{
IServiceCollection Services;
// 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 = services;
services.AddTransient<IMessage, ConsoleMessage>();
services.AddTransient<IMessage, FileMessage>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env,
IMessage message)
{
StringBuilder sb = new StringBuilder();
foreach (var item in Services)
{
if(item.ServiceType.Name.Contains("IMessage"))
{
sb.Append($"{item.ServiceType.Name} => {item.ImplementationType.Name}");
sb.Append("</br>");
}
}
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync(message.Output("Hello World!"));
await context.Response.WriteAsync(sb.ToString());
});
});
}
}
}
在這裡將會建立一個
IMessage
抽象介面型別,在該介面內僅會宣告一個方法 Output
該方法將會回傳一個字串,其中可以從回傳的字串得知這是由哪個具體實作類別所產生的;接著設計兩個類別 ConsoleMessage
與 FileMessage
,這兩個類別都會實作 IMessage
由於需要知道 .NET Core 的 DI 容器內究竟有多少筆
IMessage
介面的註冊資料,因此,宣告一個欄位在 Startup
類別內,使用 IServiceCollection Services;
語法,接著會在 ConfigureServices
方法內將 IServiceCollection
的物件設定給 Services
欄位變數。
另外,在
ConfigureServices
方法內,使用 services.AddTransient<IMessage, ConsoleMessage>(); services.AddTransient<IMessage, FileMessage>();
同時註冊兩筆同樣的介面,卻對應到不同的類別上。
另外,將會在
Configure
方法內,注入 IMessage
的具體實作物件,看看輸出結果,究竟是由哪個類別來產生的呢?還有要來檢查 IServiceCollection
的物件內,究竟有多少筆關於 IMessage
的紀錄產生呢?
現在,來進行測試看看各種不同問題:
同一個介面,註冊了不同的具體實作類別,會有甚麼情況?
若執行了上述的程式碼,會造成這個專案當掉嗎?
答案是不會喔,整體專案可以正常的執行與運作
當要進行解析的時候,究竟會取得哪個具體實作類別呢?
這樣的話,當需要注入
IMessage
介面的具體實作物件的時候,到底會注入哪個類別產生的執行個體 Instance 呢?
從執行結果可以看的出來,ASP.NET Core 的 DI 容器,將會使用最後進行註冊的 FileMessage 類別所產生的物件,因此,在這裡得到一個結論,若同一個抽象型別,註冊了多筆紀錄到 Dependency Injection Container 容器內,將會由最後註冊的紀錄取得勝利,而不是先註冊先贏喔
IServiceCollection 內,是會存在於多筆的同一個介面註冊資訊,還是會只有一個呢?
從執行結果可以清出的看到,這兩筆註冊紀錄,都存在於 DI Container 內
執行結果
File : Hello World!
IMessage => ConsoleMessage
IMessage => FileMessage
ConfigureServices
方法內使用IServiceCollection
實作物件來進行需要用到的服務註冊,若發生底下的情況,究竟會發生甚麼問題呢?