MVC框架之所以如此受歡迎的原因之一就是它十分註意支持關註分離,使各個功能部件儘量能夠相互獨立。今天我們就來看看MVC4如何使用DI方法實現一些組件的獨立,使本來結合緊密的部件,松耦合。我現在所說的對於.net的一些初學者來說可能有點拗口,其實我也是一個實打實的初學者,自己開始看這段話的時候遲遲不能... ...
MVC框架之所以如此受歡迎的原因之一就是它十分註意支持關註分離,使各個功能部件儘量能夠相互獨立。今天我們就來看看MVC4如何使用DI方法實現一些組件的獨立,使本來結合緊密的部件,松耦合。我現在所說的對於.net的一些初學者來說可能有點拗口,其實我也是一個實打實的初學者,自己開始看這段話的時候遲遲不能理解,但是當看了實例之後,消化了一下就還算是懂得了其中的一些韻味了。下麵就讓我來和大家分享一下我自己所理解的依賴性註入。希望大家能多指教。
那麼接下來我們來看一個簡單的例子,用Demo說話
我們新建一個MVC4的項目吧
然後選擇Basic模板
點擊Ok創建好項目
接著在Models文件夾添加一個IEmailSender介面,代碼如下
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DIShow.Models { public interface IEmailSender { public string SendEmail(); } }
接著再添加一個EmailSender類實現IEmailSender介面,代碼如下
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace DIShow.Models { public class EmailSender:IEmailSender { public string SendEmail() { return "My Name is SendEmail,My Type is EmailSender"; } } }
現在我們在Controller文件夾里添加一個HomeController
我們要實現的功能就是在Controller里調用SendEmail方法來發送一個郵件。
我們在controller里添加如下代碼就可以了。
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using DIShow.Models; namespace DIShow.Controllers { public class HomeController : Controller { private IEmailSender emailSender; public string Index() { emailSender = new EmailSender(); return emailSender.SendEmail(); } } }
這個時候我們運行程式,就可以看到瀏覽器里的輸出
現在我們唯一可以確定的就是這個程式時正確的。
那麼現在我們就來談談這樣簡單的一個程式可以怎樣做,來讓他更為合理,具有清晰的結構。
首先我們可以看到,我們有一個介面和一個實現這個介面的類,肯定有同學會想問,就這樣的程式幹嘛還要多此一舉搞個介面,直接在controller裡面實例化這個類,再調用SendEmail方法就好了。我想說的是,介面只是為了後面的改進做一個鋪墊,現在看來確實是可有可無。
然後現在我提出一個問題,要是我有多個發送郵件的程式,也就是說有多個類似於EmailSender這樣的類。要是我想換一個發送程式,豈不是我每次必須要修改控制器中的代碼,以此來切換髮送程式。這樣的做法對於很小的程式來說還好,對於稍微大一點的程式就會變得很不合理。這樣就把控制器的代碼變得十分繁瑣了。而且對於MVC程式來說控制器就相當於大腦,你不能總是修改大腦,最合理的方式就是修改提供程式,然後大腦只需要一個調用執行該方法的介面就行了,並不需要關心具體是怎麼實現的。回到我們現在的例子,我們要實現的效果就是在controller裡面不出現EmailSender,只需要一個IEmailSender介面。我們只需要實例化這個介面的具體實現就行了。所以我們可以對控制器中的代碼進行如下改進
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using DIShow.Models; namespace DIShow.Controllers { public class HomeController : Controller { private IEmailSender emailSender; public HomeController(IEmailSender emailSender) { this.emailSender = emailSender; } public string Index() { return emailSender.SendEmail(); } } }
我們可以看到這時我們就實現了HomeController和EmailSender之間毫無聯繫,當然這樣的程式時沒辦法運行的,因為程式並不知道如何實例化IEmailsender這個介面,雖然有一個實現了這個介面的類,但是我們並沒有告訴程式應該用哪個類去實例化這個介面。所以接下來我們就要去告訴程式應該用哪個類去實例化這個介面。
解決問題的方法就是“DI容器”,這個容器就是在介面(例如IEmailSender)和實現介面的具體類(例如EmailSender)之間擔任一個中間人,由他來處理具體通過實例化哪個類來實例化介面。
而DI容器應該如何實現呢,兩種方法,第一種就是自己創建一個DI容器,第二種就是用網上的開源代碼,本人用的是Ninject包,網址:http://www.ninject.org
而在本篇博客中,由於作者本人能力有限,所以將只演示第二種方法。
大家可以在我提供的網址上看一下該包的具體細節然後下載包來進行使用,也可以直接在VS中的引用中進行安裝。如下圖
安裝好後接下來我們就開始用使用這個包來幫助我們創建DI容器
第一步:創建一個依賴性鏈解析器
這個解析器就是類似搞出一個中介,讓程式知道哪一個類去實例化哪一個介面。我們可以在mvc項目中,新建一個文件夾,例如Infrastructure,然後在裡面建一個類:NinjectDependencyResolver 並實現IDependencyResolver介面,代碼如下
using System; using System.Collections.Generic; using System.Linq; using System.Web; using Ninject; using System.Web.Mvc; using DIShow.Models; namespace DIShow.Infrastructure { public class NinjectDependenceyResolver : IDependencyResolver { private IKernel kernel; public NinjectDependenceyResolver() { kernel = new StandardKernel(); AddBindings(); } public object GetService(Type serviceType)//IDependencyResolver的方法 { return kernel.TryGet(serviceType); } public IEnumerable<object> GetServices(Type serviceType)//IDependencyResolver的方法 { return kernel.GetAll(serviceType); } private void AddBindings() { kernel.Bind<IEmailSender>().To<EmailSender>(); } } }
第二步:註冊依賴解析器
通過註冊依賴解析器來告訴MVC框架,用戶希望使用自己的依賴解析器,那麼在哪裡註冊呢,當然是在管理整個程式運行的地方註冊-Global.asax.cs
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using DIShow.Infrastructure; namespace DIShow { // Note: For instructions on enabling IIS6 or IIS7 classic mode, // visit http://go.microsoft.com/?LinkId=9394801 public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); DependencyResolver.SetResolver(new NinjectDependenceyResolver()); } } }DependencyResolver.SetResolver(new NinjectDependenceyResolver())便實現了註冊
通過以上兩步DI容器的創建和註冊就已經搞定了。
現在我們的這個小項目的依賴性註入就完全搞定了,我們來運行程式檢測一下正確與否
事實說明是正確的。
最後我們還是來簡單總結一下吧。
首先我們來梳理一下程式的運行過程:程式啟動,根據路由系統我們到了HomeController,然後運行到HomeController構造函數的時候發現需要傳入一個IEmailSender的實例化對象。這個時候程式回到Global當中,發現Global確實註冊這樣一件事,就是我們指定了怎樣去實例化介面。通過我們的註冊信息,我們找到了我們的DI容器,也就是NinjectDependenceyResolver類。然後我們在這個類裡面傳入一個類型給GetService,然後它通過查看我們的綁定信息,這個中介就發現我們是把IEmailSender綁定到EmailSender上面去的。於是就 實例化了EmailSender得到了一個對象,最後返回給了在HomeController中的構造函數中的參數sendEmail。於是後面就可以成功執行方法了。
然後我們來看一下實現這些過程主要做了那幾步:
1.寫出介面和實現類。
2.在控制器中只調用介面方法,不出現具體類
3.創建一個DI容器,將介面和具體類綁定
4.在Global中註冊這個容器
就這樣四步就搞定了。以後要是想要切換另一個發送程式,只需要在DI容器中將介面綁定到另一個實際類上就可以了,控制器不需要做任何修改。
好啦,我的分享就到此為止了,以後要是學到了更多有意思的東西還會和大家繼續分享的。