在[ASP.NET MVC 小牛之路]系列上一篇文章(依賴注入(DI)和Ninject)的末尾提到了在ASP.NET MVC中使用Ninject要做的兩件事情,續(xù)這篇文章之后,本文將用一個實(shí)際的示例來演示Ninject在ASP.NET MVC中的應(yīng)用。
為了更好的理解和撐握本文內(nèi)容,強(qiáng)烈建議初學(xué)者閱讀本文前先閱讀依賴注入(DI)和Ninject。
本文目錄:
新建一個名為BookShop的空白解決方案。在該解決方案中分別添加一個名為BookShop.WebUI的MVC空應(yīng)用程序,和一個名為BookShop.Domain的類庫工程。目錄結(jié)構(gòu)如下:

兩個工程添加完后,在BookShop.WebUI工程下添加BookShop.Domain工程的引用。
使用NuGet分別為BookShop.WebUI工程和BookShop.Domain工程安裝Ninject包(NuGet的介紹請閱讀依賴注入(DI)和Ninject)。可以通過可視化窗口安裝,也可以打開Package Manager Console(視圖->其他窗口->Package Manager Console)執(zhí)行下面命令安裝: Install-Package Ninject -PRoject BookShop.WebUI Install-Package Ninject -Project BookShop.Domain 下圖說明安裝成功:

我們知道,在ASP.NET MVC中,一個客戶端請求是在特定Controller的Action中進(jìn)行處理的。 默認(rèn)情況下,ASP.NET MVC使用內(nèi)置的Controller工廠類 DefaultControllerFactory來創(chuàng)建某個請求對應(yīng)的Controller實(shí)例。有時候默認(rèn)的Controller工廠不能滿足我們實(shí)際的需求,我們就需要對這種默認(rèn)行為進(jìn)行擴(kuò)展,即創(chuàng)建一個繼承自DefaultControllerFactory類的自定義Controller工廠類并重寫其中的一些方法。為此,我們在BookShop.WebUI工程下創(chuàng)建一個名為Infrastructure的文件夾,在該文件夾中添加一個名為NinjectControllerFactory的工廠類,代碼如下:
public class NinjectControllerFactory : DefaultControllerFactory { private IKernel ninjectKernel; public NinjectControllerFactory() { ninjectKernel = new StandardKernel(); AddBindings(); } protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { return controllerType == null ? null : (IController)ninjectKernel.Get(controllerType); } private void AddBindings() { // todo:后面再來添加綁定 }}上面代碼中的 ninjectKernel.Get(controllerType) 可獲取到一個Controller實(shí)例。在這里如果手動實(shí)例化Controller類是一個非常復(fù)雜的過程,我們不知道Controller類有沒有帶參數(shù)的構(gòu)造函數(shù),也不知道構(gòu)造函數(shù)的參數(shù)是什么類型。而使用Ninject只需要使用上面的一個Get方法就可以,Ninject內(nèi)部會自動處理所有的依賴關(guān)系,智能地創(chuàng)建我們需要的對象。
Controller工廠類創(chuàng)建好后,我們就需要告訴MVC用我們的NinjectControllerFactory類來創(chuàng)建Controller對象,為此,需在Global.asax文件的application_Start方法中添加下面代碼:
protected void Application_Start() { ...... //設(shè)置Controller工廠 ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());}這里我們暫且不去關(guān)心上面這段代碼是什么原理,知道設(shè)置自定義的Controller工廠必須要在這注冊就行了,有空的話我會在后續(xù)博文對這部分內(nèi)容進(jìn)行更深入的講解。
在MVC應(yīng)用程序中,一切都是圍繞Domain Model(領(lǐng)域模型)來的。 所以我們在BookShop.Domain工程中專門建一個名為Entities的文件夾,用來存放領(lǐng)域?qū)嶓w模型。作為一個電子商務(wù)類的網(wǎng)上書店,當(dāng)然最重要的一個領(lǐng)域?qū)嶓w就是Book了。由于只是為了演示,我們簡單定義一個Book類,并在Entities文件夾中添加該類,代碼如下:
public class Book { public int ID { get; set; } public string Title { get; set; } public string Isbn { get; set; } public string Summary { get; set; } public string Author { get; set; } public byte[] Thumbnail { get; set; } public decimal Price { get; set; } public DateTime Published { get; set; }}我們知道,我們肯定需要一種方式來從數(shù)據(jù)庫中讀取Book數(shù)據(jù)。在這我們不防為數(shù)據(jù)的使用者(這里指Controller)提供一個IBookRepository接口,在這個接口中聲明一個IQueryable<Book>類型的屬性Books。這樣,通過該接口使用依賴注入,使用者就可以拿到Books數(shù)據(jù)集合,而不用關(guān)心數(shù)據(jù)是如何得到的。為此,我們在BookShop.Domain工程中添加一個名為 Abstract的文件夾,在該文件夾中添加我們的IBookRepository接口文件,代碼如下:
public interface IBookRepository { IQueryable<Book> Books { get; }}在MVC中我們一般會用倉儲模式(Repository Pattern)把數(shù)據(jù)相關(guān)的邏輯和領(lǐng)域?qū)嶓w模型分離,這樣對于使用者來說,通過調(diào)用倉儲對象,使用者可以直接拿到自己想要的數(shù)據(jù),而完全不必關(guān)心數(shù)據(jù)具體是如何來的。我們可以把倉儲比喻成一個超市,超市已經(jīng)為消費(fèi)者供備好了商品,消費(fèi)者只管去超市選購自己需要的商品,而完全不必關(guān)心這些商品是從哪些供應(yīng)商怎么樣運(yùn)輸?shù)匠械摹5珜τ趥}儲本身,必須要實(shí)現(xiàn)讀取數(shù)據(jù)的“渠道”。
在BookShop.Domain工程中添加一個名為Concrete文件夾用于存放具體的類。我們在Concrete文件夾中添加一個實(shí)現(xiàn)了IBookRepository接口的BookRepository類來作為我們的Book數(shù)據(jù)倉儲。BookRepository類代碼如下:
public class BookRepository : IBookRepository { public IQueryable<Book> Books { get { return GetBooks().AsQueryable(); } } private static List<Book> GetBooks() { //為了演示,這里手工造一些數(shù)據(jù),后面會介紹使用EF從數(shù)據(jù)庫中讀取。 List<Book> books = new List<Book>{ new Book { ID = 1, Title = "ASP.NET MVC 4 編程", Price = 52}, new Book { ID = 2, Title = "CLR Via C#", Price = 46}, new Book { ID = 3, Title = "平凡的世界", Price = 37} }; return books; }}為了演示,上面是手工造的一些數(shù)據(jù),后面的文章我將介紹使用Entity Framwork從數(shù)據(jù)庫中讀取數(shù)據(jù)。對于剛接觸ORM框架的朋友可能對這里IQueryable感到奇怪,為什么用IQueryable作為返回類型,而不用IEnumerable呢?后面有機(jī)會講Entity Framwork的時候再講。
打開之前我們在BookShop.WebUI工程創(chuàng)建的NinjectControllerFactory類,在AddBindings方法中添加如下代碼:
private void AddBindings() { ninjectKernel.Bind<IBookRepository>().To<BookRepository>();}這句代碼,通過Ninject把IBookRepository接口綁定到BookRepository,當(dāng)IBookRepository接口的實(shí)現(xiàn)被請求時,Ninject將自動創(chuàng)建BookRepository類的實(shí)例。
到這里,Ninject的使用步驟就結(jié)束了,接下來我們把本示例剩余的步驟完成。
右擊BookShop.WebUI工程的Controllers文件夾,添加一個名為Book的Controller,按下面代碼對其進(jìn)行編輯:
public class BookController : Controller { private IBookRepository repository; public BookController(IBookRepository bookRepository) { repository = bookRepository; }}在這,BookController的構(gòu)造函數(shù)接受了一個IBookRepository參數(shù),當(dāng)BookController被實(shí)例化的時候,Ninject就為其注入了BookRepository的依賴。接下來我們?yōu)檫@個Controller添加一個名為List的Action,用來呈現(xiàn)Book列表。代碼如下:
public class BookController : Controller { ... public ViewResult List() { return View(repository.Books); }}當(dāng)然我們需要添加一個View。右擊上面的List方法,選擇添加視圖,在彈出的窗口進(jìn)行如下配置:

然后我們在List.cshtml中用foreach循環(huán)來列舉書本信息,代碼如下:
@model IEnumerable<BookShop.Domain.Entities.Book>@{ ViewBag.Title = "Books";}@foreach (var p in Model) { <div class="item" style="border-bottom:1px dashed silver;"> <h3>@p.Title</h3> <p>價格:@p.Price.ToString("c") </p> </div> }最后我們還需要修改一下默認(rèn)路由,讓系統(tǒng)運(yùn)行后直接導(dǎo)向到我們的{controller = "Book", action = "List"},打開Global.asax文件,找到RegisterRoutes方法,進(jìn)行如下修改:
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Book", action = "List", id = UrlParameter.Optional } );}到這,我們的程序可以運(yùn)行了,效果如下:

結(jié)束語:
本文是Ninject在ASP.NET MVC中使用的一個簡單示例,目的是讓大家了解Ninject在MVC中的使用方法。當(dāng)然,Ninject的強(qiáng)大之處不僅限于本文所演示的,相信當(dāng)你熟悉了Niject之后,在搭建MVC應(yīng)用程序時,你一定會喜歡上它的。
#8樓[樓主] 2013-11-02 08:23 | Liam Wang @heren2013 BookController類的創(chuàng)建(含初始化)主要經(jīng)過下面這三個過程: 1.在Application_Start中,ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());這段注冊代碼告訴MVC用NinjectControllerFactory工廠類來創(chuàng)建所有Controller對象。 在NinjectControllerFactory類中包含了下面兩個過程:綁定接口到接口的實(shí)現(xiàn)和創(chuàng)建Controller類對象。 2.ninjectKernel.Bind<IBookRepository>().To<BookRepository>();這段綁定代碼告訴ninjectKernel當(dāng)被請求一個IBookRepository接口的實(shí)現(xiàn)時則返回一個BookRepository對象。 3.請你閱讀NinjectControllerFactory類中的GetControllerInstance方法,通過ninjectKernel.Get(controllerType)這句代碼,ninject獲取controller(如BookController)對象的信息并創(chuàng)建該controller的實(shí)例,這個過程會調(diào)用controller的構(gòu)造函數(shù),它會自動判斷構(gòu)造函數(shù)所需要的參數(shù),如BookController類的構(gòu)造函數(shù)需要一個IBookRepository接口參數(shù),根據(jù)第2個過程ninject注冊的綁定,ninject會給該構(gòu)造函數(shù)傳遞BookRepository對象(IBookRepository接口的實(shí)現(xiàn)者)的引用。
#19樓2013-11-28 09:57 | 彼年豆蔻 @Liam Wang 不需要啊,只需要在Application_Start()函數(shù)中注冊一下: DependencyResolver.SetResolver(new Code.NinjectDependencyResolver());//注冊Ioc容器 然后在具體使用中,Controller構(gòu)造注入或者使用屬性注入即可 相關(guān)代碼:namespace LvJl.WebMvc.Code{ public class NinjectDependencyResolver:System.Web.Mvc.IDependencyResolver { private readonly IKernel _kernel; public NinjectDependencyResolver() { _kernel=new StandardKernel(); AddBindings(); } private void AddBindings() { _kernel.Bind<
新聞熱點(diǎn)
疑難解答