在net core內建的DI中,提供了我們很簡便的操作方式.
但如果注入的類別一旦越來越多呢?
除了第三方套件所提供的內容外是否有其他選擇?
在ASP.Net Core中提供了內建DI(Dependency Injection)工具,
使用上也相當的方便.
網路上很多前輩有許多詳盡的教學說明,故小弟就不在此贅述.
只是在使用情境上稍做記事
在官方文件說明中
只需在Startup.cs中的ConfigureServices 將類別註冊制容器內
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<Ipay,PayImp>();
services.AddTransient<IWithdraw,WithdrawImp>();
}
但問題來了,假設在ConfigureServices這當中我有很多類別需註冊進容器內.
那不就看得很眼花,也降低了可讀性,假設如下
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<Ipay1,PayImp1>();
services.AddTransient<Ipay2,PayImp2>();
services.AddTransient<Ipay3,PayImp3>();
services.AddTransient<Ipay4,PayImp4>();
services.AddTransient<Ipay5,PayImp5>();
services.AddTransient<Ipay6,PayImp6>();
services.AddTransient<Ipay7,PayImp7>();
services.AddTransient<Ipay8,PayImp8>();
services.AddTransient<Ipay9,PayImp9>();
services.AddTransient<Ipay10,PayImp10>();
services.AddTransient<Ipay11,PayImp11>();
services.AddTransient<Ipay12,PayImp12>();
}
當然在官方文件中也有說明,可選擇替換容器服務如:Autofac
圖1
透過Autofac所提供Configuration的功能就能解決本魯的問題,
但需求上並沒有要全面使用到,而是希望能刻個簡單點的功能達到目的就好.
所以在情境上的需求是當你注入類別多的時候,不要全部放在ConfigureServices中,
而是能夠獨立開來存放.
類似Autofc中的Module.
圖2
像是SOC(關注點分離)的概念,一個實現的功能可能有多個類別放在同一個模組裡.
這樣就不會造成ConfigureServices內無限長大.
那如何透過IWebHostBuilder利用Extensions的方式,去達成這個需求?
作業環境
macOS-MoJave
開發工具
VSCODE
Net Core
2.2.100-preview1-009349
引用套件
Microsoft.AspNetCore.Hosting --version 2.1.1
Microsoft.Extensions.DependencyInjection --version 2.1.1
定義一個抽象類別與虛擬方法
public abstract class SlightModuleConfigure
{
protected virtual void Load(IServiceCollection services) { }
}
希望透過繼承SlightModuleConfigure的方式,利用Load這個虛擬方法.
各別覆寫將各功能用到的類別分別注入到容器中.
public class PayModuleConfig : SlightModuleConfigure
{
protected override void Load(IServiceCollection services)
{
services.AddSingleton<Ipay, PayImp>();
}
}
1.那如何找到所有繼承SlightModuleConfigure的類別?
2.如何再將類別加入到原本的容器中呢?
第1點.透過reflection(映射)來找出所有繼承SlightModuleConfigure的類別,並執行Load的方法
IEnumerable<Type> inhresObj = Assembly.GetEntryAssembly()
.GetTypes()
.Where(myType => myType.IsSubclassOf(typeof(SlightModuleConfigure)));
// services的部分就是指IServiceCollection這部分,會再問題2提出解釋
foreach (var item in inhresObj)
{
var instance = Activator.CreateInstance(item);
MethodInfo mi = item.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault();
mi.Invoke(instance, new object[] { services });
}
第2點.透過Net Core IWebHostBuilder 中提供了四個方法
如下圖ConfigureServices方法內提供了一個delegate讓我們可以取得原本的IServiceCollection將我們的類別加入至容器中
圖3
新增一個IWebHostBuilder Extensions
public static IWebHostBuilder UseSlightDIModuleconfig(this IWebHostBuilder webHostBuilder)
{
return webHostBuilder.ConfigureServices(services =>{
IEnumerable<Type> inhresObj = Assembly.GetEntryAssembly()
.GetTypes()
.Where(myType => myType.IsSubclassOf(typeof(SlightModuleConfigure)));
foreach (var item in inhresObj)
{
var instance = Activator.CreateInstance(item);
MethodInfo mi = item.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault();
mi.Invoke(instance, new object[] { services });
}
}
});
}
接下來就測試看看嚕
在Program.cs中的CreateWebHostBuilder加入新增的Extensions.
UseSlightDIModuleconfig();
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseSlightDIModuleconfig();
圖4
確實IWebHostBuilder 提供的 ConfigureServices 方法中有將原本的容器代入
這時候就看看是否有確實執行到所有繼承SlightModuleConfigure類別
圖5
依照圖5來看,是有被加入到原有的容器中.
接下來看看Controller內被注入的類別,是否能確實的執行
圖6圖7
利用擴充與映射的機制,做出了一個很簡單的將注入的類別做成模組抽離.
當然功能上沒有像Autofac如此的完善,不過到也符合口味不重的需求.
範例程式碼:Github
參考連結:
- IWebHostBuilder Interface
- Dependency injection in ASP.NET Core
- How to: Examine and Instantiate Generic Types with Reflection
- Autofac
- 如何在 .NET Core 使用 DI ?