依賴注入(DI)就不多說了,可以自行百度,本筆記整理自Pro ASP.NET MVC5。
1,Ninject安裝
Ninject是一個開源的注入容器,可以通過VS的Nuget進行安裝。由於是在mvc中應用,需要安裝下面3個類庫。
- Ninject
- Ninject.Web.Common
- Ninject.Web.Mvc
2,簡單使用
在沒有用DI之前,我們通常是這樣寫的。
// GET: Home public ActionResult Index() { IValueCalculator calc = new LinqValueCalculator(); ShoppingCart cart = new ShoppingCart(calc); cart.Products = products; decimal totalValue = cart.CalulateProductTotal(); return View(totalValue); }
我們的目標是,不去new LinqValueCalculator,這樣才會消除依賴,於是利用Ninject變成下面這樣。
public ActionResult Index() { IKernel ninjectKernel = new StandardKernel(); ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>(); IValueCalculator calc = ninjectKernel.Get<IValueCalculator>(); ShoppingCart cart = new ShoppingCart(calc); cart.Products = products; decimal totalValue = cart.CalulateProductTotal(); return View(totalValue); }
簡單的3句代碼,建立起了IValueCalculator和LinqValueCalculator之間的映射關系,然后調用Get方法獲得實例。
這樣做了之后,我們就不用再new了,但是這遠遠不夠,Controler類中依然存在LingValueCalculator和額外的Ninject的類,
我們需要把這些代碼移出Controler類。
3,改進Ninject,達到真正的解耦
1)在Controler的構造函數中傳入IValueCalculator
public class HomeController : Controller { private IValueCalculator calc; private Product[] products = { new Product {Name = "Kayak", Category = "Watersports", Price = 275M }, new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M }, new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.5M }, new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M } }; public HomeController(IValueCalculator calc) { this.calc = calc; } // GET: Home public ActionResult Index() { ShoppingCart cart = new ShoppingCart(calc); cart.Products = products; decimal totalValue = cart.CalulateProductTotal(); return View(totalValue); } }
難道構造函數中的IValueCalculator會自動初始化?是的,這就是Ninject幫你做的,但必須先按照下面這樣做。
2)實現IDependecyResolver接口,這個接口是MVC自帶的,不是Ninject的。實現了這個接口,MVC框架才會自動按照上面那樣創建對應接口的實例。
public class NinjectDependencyResolver : IDependencyResolver { private IKernel kernal; public NinjectDependencyResolver(IKernel kernalParam) { kernal = kernalParam; AddBindings(); } public object GetService(Type serviceType) { return kernal.TryGet(serviceType); } public IEnumerable<object> GetServices(Type serviceType) { return kernal.GetAll(serviceType); } public void AddBindings() { kernal.Bind<IValueCalculator>().To<LinqValueCalculator>().InRequestScope(); kernal.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithConstructorArgument("discount", 10m); kernal.Bind<IDiscountHelper>().To<FlexibleDiscountHelper>().WhenInjectedInto<LinqValueCalculator>(); } }
GetService和GetServices是接口的方法,大致就是傳入一個接口類型,然后傳出去一個實例。
你完全可以自己簡單的實現這兩個方法,但是Ninject畢竟是經受了考驗的,各種配置齊全的DI容器,本文的主旨也就是介紹Ninject,所以用Ninject實現這兩個方法。
具體就是上面這樣,所有的映射關系放在了AddBinding這個方法中,以后只用修改這個方法,就可以全局改變不同的實現。
3)NinjectDependencyResolver的初始化
上面的這個NinjectDependencyResolver需要初始化,在添加Njinect類庫的時候,在App_Start下,生成了一個NinjectWebCommon.cs文件,其中有一個RegisterServices方法,
這個方法會在應用程序初始化的時候調用,所以我們就在這里初始化我們的NinjectDependencyResolver。
public static class NinjectWebCommon { 。。。 /// <summary> /// Load your modules or register your services here! /// </summary> /// <param name="kernel">The kernel.</param> private static void RegisterServices(IKernel kernel) { System.Web.Mvc.DependencyResolver.SetResolver(new EssentialTools.Infrastructure.NinjectDependencyResolver(kernel)); } }
好了,到此為止,我們就可以在Controler這個類的構造函數任意的傳人需要使用的接口,只要在NinjectDependencyResolver中添加對應的映射,Ninject就會自動的幫我們創建好。
4)Ninject不是簡單的new,它可以在建立映射的時候配置不同的選項,下面例舉幾個重要的配置選項。
- 創建對象的生命周期的控制
kernal.Bind<IValueCalculator>().To<LinqValueCalculator>().InRequestScope();
- 創建對象的構造函數參數的設置(屬性值也可以設置)
kernal.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithConstructorArgument("discount", 10m);
- 建立的映射的適用條件
kernal.Bind<IDiscountHelper>().To<FlexibleDiscountHelper>().WhenInjectedInto<LinqValueCalculator>();
- 實例初始化時,又依賴於其他接口時,Ninject會把依賴的所有接口一並初始化,這一點我們自己實現的話可能會比較困難。
以上就是Ninject的簡單介紹,更加詳盡的功能,可以查閱相關文檔。另外,微軟自帶的Utinity也是和Ninject相似的DI容器,有了Ninject的使用經驗,
再用其他的DI容器應該也不是很困難。