簡介:
控制反轉:我們向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("王建"); 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("王文建"); return View(); } }