現在流行的系統一般都採用依賴註入的實現方式,利用DI容器來直接獲取所用到的類/介面的實例。.net core也一樣採用DI的方式,提供了DI容器的介面IServiceCollection,並提供了基於該介面的預設實現ServiceCollection。 這樣我們就可以不再像以前一樣,需要引入第三方的 ...
現在流行的系統一般都採用依賴註入的實現方式,利用DI容器來直接獲取所用到的類/介面的實例。.net core也一樣採用DI的方式,提供了DI容器的介面IServiceCollection,並提供了基於該介面的預設實現ServiceCollection。
這樣我們就可以不再像以前一樣,需要引入第三方的Untiy、Autofac、Castle等DI組件了。
在.net core源碼的ServiceCollectionExtensions的實現中,有三個註冊的方法AddScoped、AddSingleton、AddTransient。這其中的三個選項(Singleton、Scoped和Transient)體現三種對服務對象生命周期的控制形式。
- Singleton:ServiceProvider創建的服務實例保存在作為根節點的ServiceProvider上,所有具有同一根節點的所有ServiceProvider提供的服務實例均是同一個對象。適合於單例模式。
- Scoped:ServiceProvider創建的服務實例由自己保存,所以同一個ServiceProvider對象提供的服務實例均是同一個對象。 可以簡單的認為是每請求(Request)一個實例。
- Transient:針對每一次服務提供請求,ServiceProvider總是創建一個新的服務實例。 每次訪問時被創建,適合輕量級的,無狀態的服務。
這個具體說起來就太多了,還是查閱相關材料吧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容器。
面向雲的.net core開發框架