ASP.NET Core 透過擴充方法將 ConfigureServices 中注入的類別分類

在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

參考連結:

  1. IWebHostBuilder Interface
  2. Dependency injection in ASP.NET Core
  3. How to: Examine and Instantiate Generic Types with Reflection
  4. Autofac
  5. 如何在 .NET Core 使用 DI ?