前情提要 由前兩篇的議題延續下去,參考[DotnetCore]FTP-下載上傳 、[DotnetCore]SFTP-下載上傳 ,因為同樣都是實作IFtpService
,同一個專案皆有使用到Ftp及SFtp則會面臨到此篇要解決的問題,該如何分辨要採用哪種實作的下載、上傳,有哪些方法可以解決,由筆者我來細說吧,主要參考 國外文章 :佛心的整理了五種不同的解決方式。
內容 Collection方式 效能有憂慮,基本上是注入時直接產出多個實作實體,依照條件選取其中一個,因此會有不需要用到的實體產出,屬比較不建議的做法。這個方式需要有一個可以識別的屬性欄位,因此改造一下IFtpService
、FtpService
、SFtpService
,多加一個Name屬性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public interface IFtpService { string Name { get ; } } public class FtpService : IFtpService { public string Name { get ; } public FtpService (IFilePathService filePathService, IConfiguration config ) { this .Name = "F" ; } }
註冊Service 1 2 3 4 5 6 7 public void ConfigureServices (IServiceCollection services ){ services.AddTransient<IFtpService, FtpService>(); services.AddTransient<IFtpService, SFtpService>(); }
注入Service 製作一個假的Service,主要參考注入方式即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public interface IExchangeService { void DoWork () ; } public class ExchangeService : IExchangeService { private readonly IFtpService _ftpService; public ExchangeService (IEnumerable<IFtpService> ftpServiceCollection ) { _ftpService = ftpServiceCollection.SingleOrDefault(x => x.Name == "F" ); } public void DoWork () { throw new System.NotImplementedException(); } }
Resolver方式 撰寫IFtpServiceResolver 宣告IFtpServiceResolver
並實作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public interface IFtpServiceResolver { IFtpService Resolve (string serviceName ) ; } public class FtpServiceResolver : IFtpServiceResolver { private readonly IServiceProvider _provider; public FtpServiceResolver (IServiceProvider provider ) { _provider = provider; } public IFtpService Resolve (string serviceName ) { var type = Assembly.GetAssembly(typeof (FtpServiceResolver)).GetType(serviceName); var instance = this ._provider.GetService(type); return instance as IFtpService; } }
主要是注入IServiceProvider
,透過Resolve
方法傳入的ServiceName
,透過注入得到的IServiceProvider
取得對應的實體並做事。
註冊Service 註冊方式跟第一種Collection
不太一樣,此種方式是透過Resolver
建立實體,因此直接註冊實作Service即可。
1 2 3 4 5 6 7 8 public void ConfigureServices (IServiceCollection services ){ services.AddTransient<FtpService>(); services.AddTransient<SFtpService>(); services.AddTransient<IFtpServiceResolver, FtpServiceResolver>(); }
注入Service 改寫ExchangeService
,這次改注IFtpServiceResolver
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class ExchangeService : IExchangeService { private readonly IFtpService _ftpService; public ExchangeService (IFtpServiceResolver ftpServiceResolver ) { _ftpService = ftpServiceResolver.Resolve("FtpService" ); } public void DoWork () { throw new System.NotImplementedException(); } }
Resolver + Factory Pattern 筆者看了一下,基本上是跟上述Resolver是一樣的,只是將透過IServiceProvider
取得實作Service
改成使用Activator.CreateInstance
的方式取得實作Service
,基本上筆者不推薦這種,因為這樣就沒有使用到DI機制自動注入的好處,然而因為這邊負責建立實體,因此屬工廠模式
。
1 2 3 4 5 6 7 8 9 10 public class FtpServiceResolver : IFtpServiceResolver { public IFtpService Resolve (string serviceName ) { var type = Assembly.GetAssembly(typeof (FtpServiceResolver)).GetType(serviceName); var instance = Activator.CreateInstance(type); return instance as IFtpService; } }
Resolver + Delegate 筆者最喜歡這個方式,由外部決定取得實作Service
方式,而不是寫死於Resolver
中,由外部決定做法,因為dotnet core
的注入宣告於Startup
中,因此將實際做法宣告於Startup
中。
註冊Service 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public delegate IFtpService FtpServiceDelegate (string serviceName ) ;public void ConfigureServices (IServiceCollection services ){ services.AddTransient<FtpService>(); services.AddTransient<SFtpService>(); services.AddTransient<FtpServiceDelegate>(provider => serviceName => { var type = Assembly.GetAssembly(typeof (FtpServiceResolver)).GetType(serviceName); var instance = provider.GetService(type); return instance as IFtpService; }); }
注入Service 改造一下ExchangeService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 using static {namespace }.Startup ;public class ExchangeService : IExchangeService { private readonly IFtpService _ftpService; public ExchangeService (FtpServiceDelegate ftpServiceDelegate ) { _ftpService = ftpServiceDelegate("FtpService" ); } public void DoWork () { throw new System.NotImplementedException(); } }
Implicit Delegate + Lambda Function 第五種其實就是第四種的變種,直接注入Func,於註冊時宣告取得實作Service方式,直接注入相對應的Func取得實作Service。
註冊Service 1 2 3 4 5 6 7 8 9 10 11 12 13 public void ConfigureServices (IServiceCollection services ){ services.AddTransient<FtpService>(); services.AddTransient<SFtpService>(); services.AddTransient<Func<string , IFtpService>>(provider => serviceName => { var type = Assembly.GetAssembly(typeof (FtpServiceResolver)).GetType(serviceName); var instance = provider.GetService(type); return instance as IFtpService; }); }
注入Service 改造一下ExchangeService
,不過筆者覺得這種直接注Func醜醜的,筆者本身不會選擇這種方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class ExchangeService : IExchangeService { private readonly IFtpService _ftpService; public ExchangeService (Func<string , IFtpService> func ) { _ftpService = func("FtpService" ); } public void DoWork () { throw new System.NotImplementedException(); } }
結論 看完文章才知道有這麼多取得實作Service
的方式,對筆者來說,開了眼界,筆者自己本身會選擇第四種作為主要使用方式,也實際應用於專案中,除了第一種Collection
與第三種FactoryPattern
之外,其他筆者都覺得還不錯,不過就是看各位讀者的使用情境去選擇最適合的一種即可。
參考
https://devkimchi.com/2020/07/01/5-ways-injecting-multiple-instances-of-same-interface-on-aspnet-core/