簡介:
控制反轉:我們向IOC容器發出獲取一個物件例項的一個請求,IOC容器便把這個物件例項“注入”到我們的手中,在這個過程中你不是一個控制者而是一個請求者,依賴於容器提供給你的資源,控制權落到了容器身上。這個過程就是控制反轉。
依賴注入:我們向容器發出請求以後,獲得這個物件例項的過程就叫依賴注入。
關於Ioc的框架有很多,比如astle Windsor、Unity、Spring.NET、StructureMap,我們這邊使用微軟提供的Unity做示例,你可以使用 Nuget 新增 Unity ,也可以引用Microsoft.Practices.Unity.dll和Microsoft.Practices.Unity.Configuration.dll,下面我們就一步一步的學習下 Unity依賴注入 的詳細使用。
一、使用 Nuget 新增 Unity
二、實現構造注入
新增一個介面和一個實現類,通過Main()方法呼叫測試。
/// <summary> /// 顯示資訊 /// </summary> public interface IUserDao { void Display(string mes); } class UserImpl : IUserService { public IUserDao IUserDao; //建構函式設定值 public UserImpl(IUserDao UserDao) { IUserDao = UserDao; } /// <summary> /// 顯示資訊 /// </summary> /// <param name="mes"></param> public void Display(string mes) { IUserDao.Display(mes); } }
/// <summary> /// 顯示資訊 /// </summary> public interface IUserDao { void Display(string mes); } public class UserDaoImpl : IUserDao { public void Display(string mes) { Console.WriteLine(mes); } }
class Program { public IUserService IUserService { get; set; } public static void Main(string[] args) { //建立容器 UnityContainer container = new UnityContainer(); //註冊依賴物件 container.RegisterType<IUserService, UserImpl>(); container.RegisterType<IUserDao, UserDaoImpl>(); //返回撥用者 IUserService IUser = container.Resolve<UserImpl>(); //執行 IUser.Display("hello"); Console.ReadLine(); } }
點選執行,成功輸出。
構造器注入 構造器注入(Constructor Injection):IoC容器會智慧地選擇選擇和呼叫適合的建構函式以建立依賴的物件。 如果被選擇的建構函式具有相應的引數,IoC容器在呼叫建構函式之前解析註冊的依賴關係並自行獲得相應引數物件。 RegisterType:可以看做是自來水廠決定用什麼作為水源,可以是水庫或是地下水,我只要“註冊”開關一下就行了。 Resolve:可以看做是自來水廠要輸送水的物件,可以是農村或是城市,我只要“控制”輸出就行了。
三、屬性注入
屬性注入(Property Injection),就是通過 set 設值對物件進行設值,只需要在呼叫物件的上面加上 [Dependency] 標記即可。當依賴物件被容器初始化以後,會自動對該物件設值。
class UserImpl : IUserService { //只需要在物件成員前面加上[Dependency], //就是把建構函式去掉,成員物件上面加[Dependency]注入 [Dependency] public IUserDao IUserDao { get; set; } //public UserImpl(IUserDao UserDao) //{ // IUserDao = UserDao; //} /// <summary> /// 顯示資訊 /// </summary> /// <param name="mes"></param> public void Display(string mes) { IUserDao.Display(mes); } }
點選執行,實現的結果是一樣的。
配置檔案註冊:
其實使用上面 RegisterType 方法進行註冊,每次新增和刪除一個註冊都需要去修改程式碼和重新編譯,這樣不符合“高內聚、低耦合”的程式設計思想,所以我們可以採用配置檔案的方式去註冊,這樣每次新增和修改註冊就不需要去修改程式碼和重新發布了。配置檔案註冊用 UnityConfigurationSection 的 Configure載入配置檔案註冊。
程式碼(如果是控制檯程式,配置寫在App.config,如果是Web程式,就寫在 Web.config):
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" /> </configSections> <unity xmlns="http://schemas.microsoft.com/practces/2010/unity"> <containers> <!--MyContainer為自定義名稱 只需要和呼叫時名稱保持一致即可--> <container name="MyContainer"> <!--type為物件的名稱,mapTo為注入物件的名稱 寫法為用逗號隔開兩部分,一是類的全部,包括名稱空間,二是程式集名稱--> <register type="ThreadDemo.Bll.IUserBll,ThreadDemo" mapTo="ThreadDemo.Bll.impl.UserBll,ThreadDemo"> <lifetime type="singleton" /> </register> <register type="ThreadDemo.Dal.IUserDal,ThreadDemo" mapTo="ThreadDemo.Dal.impl.UserDal,ThreadDemo"/> </container> </containers> </unity> <!--startup必須放在<configSections>節點下面,否則報錯--> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /> </startup> </configuration>
class Program { public IUserBll UserBll { get; set; } public static void Main(string[] args) { //建立容器 //UnityContainer container = new UnityContainer(); //註冊依賴物件 //container.RegisterType<IUserService, UserImpl>(); //container.RegisterType<IUserDao, UserDaoImpl>(); //返回撥用者 //IUserService IUser = container.Resolve<UserImpl>(); //建立容器 UnityContainer container = new UnityContainer(); UnityConfigurationSection config = (UnityConfigurationSection)ConfigurationManager.GetSection(UnityConfigurationSection.SectionName); //載入到容器 config.Configure(container, "MyContainer"); //返回撥用者 IUserBll IUser = container.Resolve<IUserBll>(); //執行 IUser.Display("王建"); Console.ReadLine(); } }
四、方法注入
方法注入和構造注入差不多,只不過把建構函式變成了一個普通的方法,在方法前面加 [InjectionMethod] 屬性。
namespace ThreadDemo.Bll.impl { public class UserBll : IUserBll { public IUserDal IDal; /// <summary> /// 方法注入-加[InjectionMethod]屬性 /// </summary> /// <param name="IUserDal"></param> [InjectionMethod] public void SetInjection(IUserDal IUserDal) { IDal = IUserDal; } /// <summary> /// 顯示資訊 /// </summary> /// <param name="mes"></param> public void Display(string mes) { IDal.Display(mes); } } }
這幾種方法執行結果都是一樣的。
五、Unity.MVC在Web中的應用
下面的例子是在Unity在Web專案中的使用:
1、安裝Unity.MVC
2、在目錄下會生成一個 BootStrapper.cs 的類檔案,開啟進行 編輯(如果沒有生成,自己建立,名稱隨意)。
namespace ShowWeatherWebUI { public class BootStrapper { /// <summary> /// 獲取容器-註冊依賴關係 /// </summary> /// <returns></returns> public static IUnityContainer Initialise() { var container = BuildUnityContainer(); DependencyResolver.SetResolver(new UnityDependencyResolver(container)); return container; } /// <summary> /// 載入容器 /// </summary> /// <returns></returns> private static IUnityContainer BuildUnityContainer() { var container = new UnityContainer(); RegisterTypes(container); return container; } /// <summary> /// 實施依賴注入 /// </summary> /// <param name="container"></param> private static void RegisterTypes(UnityContainer container) { //依賴關係可以選擇程式碼形式,也可以用配置檔案的形式 //UnityConfigurationSection config = (UnityConfigurationSection)ConfigurationManager.GetSection(UnityConfigurationSection.SectionName); //載入到容器 //config.Configure(container, "MyContainer"); container.RegisterType<IUerBll, UerBll>(); container.RegisterType<IUserDal, UserDal>(); } } }
3、在 Global.asax 檔案中新增 BootStrapper.Initialise() 的方法。
因為Global.asax是應用程式啟動的時候會執行,所以會去載入容器
public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); //載入容器-註冊依賴 BootStrapper.Initialise(); } }
4、在Controller裡呼叫,
在每個呼叫的介面新增 [Dependency] 屬性即可,也就是屬性輸入,也可以採用建構函式注入和方法注入。
public class HomeController : Controller { [Dependency] public IUerBll bll { get; set; } public ActionResult Index() { bll.Display("hello"); return View(); } }