現在流行的系統一般都采用依賴注入的實現方式,利用DI容器來直接獲取所用到的類/接口的實例。.net core也一樣采用DI的方式,提供了DI容器的接口IServiceCollection,並提供了基於該接口的缺省實現ServiceCollection。
這樣我們就可以不再像以前一樣,需要引入第三方的Untiy、Autofac、Castle等DI組件了。
在.net core源碼的ServiceCollectionExtensions的實現中,有三個注冊的方法AddScoped、AddSingleton、AddTransient。這其中的三個選項(Singleton、Scoped和Transient)體現三種對服務對象生命周期的控制形式。
這個具體說起來就太多了,還是查閱相關材料吧J
有了DI容器,我們在使用時,可以簡單的在Startup.cs程序中編寫上注冊語句,下面以操作日志的倉儲類為例:
1 public void ConfigureServices(IServiceCollection services) 2 { 3 services.AddScoped<IOperLogRepository, OperLogRepository>(); 4 }
第一個泛型類型表示將要從容器中請求的類型(通常是一個接口)。第二個泛型類型表示將由容器實例化並且用於完成這些請求的具體類型。
我們在操作日志的邏輯服務類使用操作日志的倉儲時,就這樣寫:
1 public partial class OperLogAppService : IOperLogAppService 2 { 3 private IOperLogRepository service; 4 5 public OperLogAppService(IOperLogRepository service) 6 { 7 this.service = service; 8 } 9 10 public void Write(OperLogDto operLogDto) 11 { 12 this.service.Insert(operLogDto.AsInfo()); 13 } 14 }
系統會在創建OperLogAppService的實例時,會自動創建IOperLogRepository在DI容器中注冊OperLogRepository類的實例。
然而這種方式在框架中是有些不方便的。我們知道,框架要求的是可擴展,可配置,在新增系統功能模塊時,還需要手工修改程序,在Startup.cs的ConfiguraeService中增加注冊(就算把所有注冊移到一個獨立的方法中也是一樣),就如同第一個代碼寫的一樣,包括注冊倉儲類、注冊邏輯服務類等等。然後編譯,再發布運行。每增加、修改一個模塊,都需要整個系統編譯、發布,對運行中的系統影響還是挺大的。
我們的做法是,功能模塊都有一個自注冊的類。這個自注冊的類會將功能模塊中所有的倉儲類、邏輯服務類等都注冊進DI容器中。Startup.cs會自動查找所有功能模塊中的自注冊類,然後將相關內容注冊進DI容器中。這樣就可以做到,每增加一個模塊,只需要將該模塊的應用程序集Dll復制到系統的運行目錄就行了,其他的系統幫你搞定。
按照上述思路,首先是建一個自注冊的類。這個類都抽象出一個接口IServiceRegister
1 public interface IServiceRegister 2 { 3 /// <summary> 4 /// 注冊 5 /// </summary> 6 void Register(IServiceCollection services); 7 }
我們以通用模塊為例子,自注冊的類應該是這樣的。
1 public class CommonServiceRegister : IServiceRegister 2 { 3 public void Register(IServiceCollection services) 4 { 5 services.AddDbContext<CommonDbContext>(option => option.UseDb<CommonDbContext>(services.BuildServiceProvider()), ServiceLifetime.Scoped); 6 7 services.AddScoped<IParaReferRepository, ParaReferRepository>(); 8 services.AddScoped<IParaReferAppService, ParaReferAppService>(); 9 services.AddScoped<ISystemParameterRepository, SystemParameterRepository>(); 10 services.AddScoped<ISystemParameterAppService, SystemParameterAppService>(); 11 … 12 } 13 }
這個類中service.AppScoped注冊的是倉儲層和邏輯層接口對應的實現類,我們這裡只是列出了系統參數和引用參數的注冊。對於第一句AddDbContext,請參見:4.4 異構、多數據庫的存取組件
為了在Startup.cs中自己查找並調用這些注冊類,還是比較簡單的,程序如下:調用ReflectionHelper的GetSubTypes方法,獲取所有繼承IServiceRegister的實現類,對於每個實現類,創建實例並將模塊的倉儲和邏輯服務注冊到DI容器中。ReflectionHelper的GetSubTypes方法,請參見:4.1 反射工具
1 IEnumerable<Type> serviceList = ReflectionHelper.GetSubTypes<IServiceRegister>(); 2 foreach (Type type in serviceList) 3 { 4 IServiceRegister register = ReflectionHelper.CreateInstance(type) as IServiceRegister; 5 6 register.Register(services); 7 }
.net framework也可以按照上述思路進行,不同的就是.net framework沒有startup.cs,只需要將上面的內容寫進global.asax即可,還有一點不同是IServiceCollection,可以使用Unity等DI組件提供的DI容器。