上一篇《我在項目中運用 IOC(依賴注入)--入門篇》只是簡單的使用 IOC。實際項目使用 IOC 的情景復雜多了,比如說,構造函數有多個參數,有多個類繼承同一個接口... Unity都有解決方法。首先回顧一下入門篇的項目需求:項目中數據統計功能,它下面有三種不同的統計類型,需要與數據庫交互,然后展示到頁面,在這篇中我們接着這個需求繼續擴充。
【沒有接口】
新增Model 層,LoginUser 當前登錄人。無接口的怎樣用IOC 創建對象。代碼如下
Model.LoginUser loginUser = container.Resolve<Model.LoginUser>();
【多個子類】
DAL 層 新增ShowResult2 方法
public class Analyse:IDAL.IAnalyse { public void ShowResult() { Console.WriteLine("分析底層數據庫交互"); } public void ShowResult2() { Console.WriteLine("這是B類分析底層數據庫交互"); } }
BLL層 新增 AnalyseB ,繼承 IAnalyse
public class AnalyseB:IBLL.IAnalyse { ////使用依賴注入 [Dependency] public IDAL.IAnalyse dal { get; set; } public void ShowResult() { dal.ShowResult2(); } }
按照入門篇的步驟,需要在core 層 DependencyRegister 修改 DependencyRegisterContainer 方法
public static IUnityContainer DependencyRegisterContainer() { IUnityContainer container = new UnityContainer(); container.RegisterType<IBLL.IAnalyse, BLL.Analyse>() .RegisterType<IDAL.IAnalyse, DAL.Analyse>() .RegisterType<IBLL.IAnalyse, BLL.AnalyseB>(); return container; }
我們發現IBLL.IAnalyse被兩個類繼承,所以在 IOC 容器中注入了兩次。這樣寫能區分開嗎?我們先在UI 層增加顯示代碼
/// <summary> /// 多個類繼承同一個接口 /// </summary> private static void IOCMethod4() { IUnityContainer container = DependencyRegister.DependencyRegisterContainer(); IBLL.IAnalyse bll = container.Resolve<IBLL.IAnalyse>(); bll.ShowResult(); }
運行程序,發現每次只會出現 AnalyseB 類的結果。即 IAnalyse=最后注冊的類型 。Unity 的 RegisterType 方法有個參數string name。用key來區分子類型。修改代碼
//core 層 public static IUnityContainer DependencyRegisterContainer() { IUnityContainer container = new UnityContainer(); container.RegisterType<IBLL.IAnalyse, BLL.Analyse>() .RegisterType<IDAL.IAnalyse, DAL.Analyse>() .RegisterType<IBLL.IAnalyse, BLL.AnalyseB>("Analyse-B"); return container; } //// UI 層 private static void IOCMethod4() { IUnityContainer container = DependencyRegister.DependencyRegisterContainer(); IBLL.IAnalyse bll = container.Resolve<IBLL.IAnalyse>("Analyse-B"); bll.ShowResult(); } ////同時運行 IOCMethod2() 和 IOCMethod4()
結果
這樣就是實現了子類多個的 IOC 容器注冊。(注意,key一定要區分大小寫)
【帶參的構造函數】
Web程序與數據庫交互的時候,需要從前台獲取查詢條件,我在 AnalyseB 中寫了一個帶有 string 類型的構造函數。代碼如下
public class AnalyseB:IBLL.IAnalyse { public string StrWhere { get; set; } /// <summary> /// 查詢條件的構造函數 /// </summary> /// <param name="strWhere"></param> public AnalyseB(string strWhere) { this.StrWhere = strWhere; } ////使用依賴注入 [Dependency] public IDAL.IAnalyse dal { get; set; }
public void ShowResult() { dal.ShowResult2(StrWhere); } }
//// 修改 core 層 容器注冊方法 public static IUnityContainer DependencyRegisterContainer() { IUnityContainer container = new UnityContainer(); container.RegisterType<IBLL.IAnalyse, BLL.Analyse>() .RegisterType<IDAL.IAnalyse, DAL.Analyse>() .RegisterType<IBLL.IAnalyse, BLL.AnalyseB>("Analyse-B", (new InjectionConstructor(typeof(string)))); return container; } ////UI 層調用代碼如下 private static void IOCMethod4() { IUnityContainer container = DependencyRegister.DependencyRegisterContainer(); IBLL.IAnalyse bll = container.Resolve<IBLL.IAnalyse>("Analyse-B", new ParameterOverride("strWhere", " WHERE 1=1 ")); bll.ShowResult(); }
AnalyseB 構造函數帶有string 類型的參數,在 容器注冊的時候,需要注冊 參數的類型 new InjectionConstructor(typeof(string),typeof(param2),...) (這里也可以是常量)。調用時用ParameterOverride(構造函數參數名,參數值) 來重新給參數賦值。
如果參數是其它類型又怎么辦。例如數據以表格形式呈現時,需要分頁。我在BLL 層加了一個分頁接口。修改代碼
////新增分頁接口 public interface IPager { string GetPager(); } public class AnalysePager : IBLL.IPager { public string GetPager() { return "數據已分頁"; } } public class AnalyseB:IBLL.IAnalyse { public string StrWhere { get; set; } public IBLL.IPager Pager { get; set; } public AnalyseB(IBLL.IPager pager, string strWhere) { this.Pager = pager; this.StrWhere = strWhere + pager.GetPager(); } ////使用依賴注入 [Dependency] public IDAL.IAnalyse dal { get; set; } public void ShowResult() { dal.ShowResult2(StrWhere); } } ////修改core層 方法 public class DependencyRegister { public static IUnityContainer DependencyRegisterContainer() { IUnityContainer container = new UnityContainer(); container.RegisterType<IBLL.IAnalyse, BLL.Analyse>() .RegisterType<IDAL.IAnalyse, DAL.Analyse>() .RegisterType<IBLL.IPager,BLL.AnalysePager>() .RegisterType<IBLL.IAnalyse, BLL.AnalyseB>("Analyse-B", (new InjectionConstructor(typeof(IBLL.IPager) , typeof(string)))); return container; } } ////UI 層代碼不變
運行結果:
【屬性注入】
修改一下構造函數,把參數 IPager 提成屬性。 代碼如下
public class AnalyseB:IBLL.IAnalyse { public string StrWhere { get; set; } public IBLL.IPager Pager { get; set; } /// <summary> /// 查詢條件的構造函數 /// </summary> /// <param name="strWhere"></param> public AnalyseB(IBLL.IPager pager, string strWhere) { this.Pager = pager; this.StrWhere = strWhere + pager.GetPager(); }
public AnalyseB(string strWhere) { this.StrWhere = strWhere; } ////使用依賴注入 //[Dependency] public IDAL.IAnalyse dal { get; set; } public void ShowResult() { StrWhere += Pager.GetPager(); dal.ShowResult2(StrWhere); } //// core public static IUnityContainer DependencyRegisterContainer() { IUnityContainer container = new UnityContainer(); container.RegisterType<IBLL.IAnalyse, BLL.Analyse>() .RegisterType<IDAL.IAnalyse, DAL.Analyse>() .RegisterType<IBLL.IPager, BLL.AnalysePager>() //.RegisterType<IBLL.IAnalyse, BLL.AnalyseB>("Analyse-C", (new InjectionConstructor(typeof(IBLL.IPager) , typeof(string)))); .RegisterType<IBLL.IAnalyse, BLL.AnalyseB>("Analyse-D", new InjectionMember[] { new InjectionConstructor(typeof (string)), new InjectionProperty("Pager"), new InjectionProperty("dal"), }); return container; }
自己在實際使用過程總結了幾點:
1.使用屬性注入,屬性不能再構造函數中使用,否則會報為實例化對象異常。(關於這點自己持懷疑態度,有可能是自己不正確的使用造成的。希望和大家討論)
2.BLL.AnalyseB 本身有個屬性 dal. 如果使用屬性注入,則所有屬性都須加入 InjectionMember[] .
3.最好使用構造注入。
(以上三點是在項目使用過程中,自己總結出來。非權威,非標准,有不對的地方希望大家指出。`(*∩_∩*)′)
【方法注入】
除開構造注入,屬性注入,還有方法注入。使用與屬性注入差不多。方法注入尚沒有在實際項目中應用過,下面例子是參考其它兩種注入方法寫的
////修改此類,增加字段_pager 和 方法 SetPager public class AnalyseB:IBLL.IAnalyse { public string StrWhere { get; set; } public IBLL.IPager Pager { get; set; } public IBLL.IPager _pager; public void SetPager(IBLL.IPager pager) { this._pager = pager; } /// <summary> /// 查詢條件的構造函數 /// </summary> /// <param name="strWhere"></param> public AnalyseB(IBLL.IPager pager, string strWhere) { this.Pager = pager; this.StrWhere = strWhere + pager.GetPager(); } public AnalyseB(string strWhere) { this.StrWhere = strWhere; } ////使用依賴注入 [Dependency] public IDAL.IAnalyse dal { get; set; } public void ShowResult() { //StrWhere += Pager.GetPager(); StrWhere += this._pager.GetPager(); dal.ShowResult2(StrWhere); } ////增加core層的注入方式 container..RegisterType<IBLL.IAnalyse, BLL.AnalyseB>("Analyse-E", new InjectionMember[] { new InjectionConstructor(typeof (string)), new InjectionMethod("SetPager",container.Resolve<IBLL.IPager>()), }); ////UI 層調用 /// <summary> /// 方法注入 /// </summary> public static void IOCMethod6() { IUnityContainer container = DependencyRegister.DependencyRegisterContainer(); IBLL.IAnalyse bll = container.Resolve<IBLL.IAnalyse>("Analyse-E", new ParameterOverride("strWhere", " WHERE 1=1 ")); bll.ShowResult(); }
(個人覺得方法注入很雞肋,其實就是屬性注入)
以上是我在項目運用過程中總結出來的一些心得,尚有許多不足的地方,先拋磚引玉,與大家共同討論,共同學習。