[ASP.NET MVC 小牛之路]05 - 使用 Ninject

在[ASP.NET MVC 小牛之路]系列上一篇文章(依賴注入(DI)和Ninject)的末尾提到了在ASP.NET MVC中使用Ninject要作的兩件事情,續這篇文章以後,本文將用一個實際的示例來演示Ninject在ASP.NET MVC中的應用。html

爲了更好的理解和撐握本文內容,強烈建議初學者閱讀本文前先閱讀依賴注入(DI)和Ninject數據庫

本文目錄:編程

準備工做

新建一個名爲BookShop的空白解決方案。在該解決方案中分別添加一個名爲BookShop.WebUI的MVC空應用程序,和一個名爲BookShop.Domain的類庫工程。目錄結構以下:框架

兩個工程添加完後,在BookShop.WebUI工程下添加BookShop.Domain工程的引用。ide

使用NuGet分別爲BookShop.WebUI工程和BookShop.Domain工程安裝Ninject包(NuGet的介紹請閱讀依賴注入(DI)和Ninject)。能夠經過可視化窗口安裝,也能夠打開Package Manager Console(視圖->其餘窗口->Package Manager Console)執行下面命令安裝:
Install-Package Ninject -Project BookShop.WebUI
Install-Package Ninject -Project BookShop.Domain
下圖說明安裝成功:函數

建立Controller Factory

咱們知道,在ASP.NET MVC中,一個客戶端請求是在特定Controller的Action中進行處理的。 默認狀況下,ASP.NET MVC使用內置的Controller工廠類 DefaultControllerFactory來建立某個請求對應的Controller實例。有時候默認的Controller工廠不能知足咱們實際的需求,咱們就須要對這種默認行爲進行擴展,即建立一個繼承自DefaultControllerFactory類的自定義Controller工廠類並重寫其中的一些方法。爲此,咱們在BookShop.WebUI工程下建立一個名爲Infrastructure的文件夾,在該文件夾中添加一個名爲NinjectControllerFactory的工廠類,代碼以下:post

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實例。在這裏若是手動實例化Controller類是一個很是複雜的過程,咱們不知道Controller類有沒有帶參數的構造函數,也不知道構造函數的參數是什麼類型。而使用Ninject只須要使用上面的一個Get方法就能夠,Ninject內部會自動處理全部的依賴關係,智能地建立咱們須要的對象。ui

Controller工廠類建立好後,咱們就須要告訴MVC用咱們的NinjectControllerFactory類來建立Controller對象,爲此,需在Global.asax文件的Application_Start方法中添加下面代碼:url

protected void Application_Start() {
    ......

    //設置Controller工廠
    ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
}

這裏咱們暫且不去關心上面這段代碼是什麼原理,知道設置自定義的Controller工廠必需要在這注冊就好了,有空的話我會在後續博文對這部份內容進行更深刻的講解。spa

添加Domain Model

在MVC應用程序中,一切都是圍繞Domain Model(領域模型)來的。 因此咱們在BookShop.Domain工程中專門建一個名爲Entities的文件夾,用來存放領域實體模型。做爲一個電子商務類的網上書店,固然最重要的一個領域實體就是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; }
}

添加Repository

咱們知道,咱們確定須要一種方式來從數據庫中讀取Book數據。在這咱們不防爲數據的使用者(這裏指Controller)提供一個IBookRepository接口,在這個接口中聲明一個IQueryable<Book>類型的屬性Books。這樣,經過該接口使用依賴注入,使用者就能夠拿到Books數據集合,而不用關心數據是如何獲得的。爲此,咱們在BookShop.Domain工程中添加一個名爲 Abstract的文件夾,在該文件夾中添加咱們的IBookRepository接口文件,代碼以下:

public interface IBookRepository {
    IQueryable<Book> Books { get; }
}

在MVC中咱們通常會用倉儲模式(Repository Pattern)把數據相關的邏輯和領域實體模型分離,這樣對於使用者來講,經過調用倉儲對象,使用者能夠直接拿到本身想要的數據,而徹底沒必要關心數據具體是如何來的。咱們能夠把倉儲比喻成一個超市,超市已經爲消費者供備好了商品,消費者只管去超市選購本身須要的商品,而徹底沒必要關心這些商品是從哪些供應商怎麼樣運輸到超市的。但對於倉儲自己,必需要實現讀取數據的「渠道」。

在BookShop.Domain工程中添加一個名爲Concrete文件夾用於存放具體的類。咱們在Concrete文件夾中添加一個實現了IBookRepository接口的BookRepository類來做爲咱們的Book數據倉儲。BookRepository類代碼以下:

public class BookRepository : IBookRepository {

    public IQueryable<Book> Books {
        get { return GetBooks().AsQueryable(); }
    }

    private static List<Book> GetBooks() {  
        //爲了演示,這裏手工造一些數據,後面會介紹使用EF從數據庫中讀取。
        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;
    }
}

爲了演示,上面是手工造的一些數據,後面的文章我將介紹使用Entity Framwork從數據庫中讀取數據。對於剛接觸ORM框架的朋友可能對這裏IQueryable感到奇怪,爲何用IQueryable做爲返回類型,而不用IEnumerable呢?後面有機會講Entity Framwork的時候再講。

添加綁定

打開以前咱們在BookShop.WebUI工程建立的NinjectControllerFactory類,在AddBindings方法中添加以下代碼:

private void AddBindings() {
    ninjectKernel.Bind<IBookRepository>().To<BookRepository>();
}

這句代碼,經過Ninject把IBookRepository接口綁定到BookRepository,當IBookRepository接口的實現被請求時,Ninject將自動建立BookRepository類的實例。

到這裏,Ninject的使用步驟就結束了,接下來咱們把本示例剩餘的步驟完成。

顯示列表

右擊BookShop.WebUI工程的Controllers文件夾,添加一個名爲Book的Controller,按下面代碼對其進行編輯:

public class BookController : Controller {
    private IBookRepository repository;

    public BookController(IBookRepository bookRepository) {
        repository = bookRepository;
    }
}

在這,BookController的構造函數接受了一個IBookRepository參數,當BookController被實例化的時候,Ninject就爲其注入了BookRepository的依賴。接下來咱們爲這個Controller添加一個名爲List的Action,用來呈現Book列表。代碼以下:

public class BookController : Controller {
    ...

    public ViewResult List() {
        return View(repository.Books);
    }
}

固然咱們須要添加一個View。右擊上面的List方法,選擇添加視圖,在彈出的窗口進行以下配置:

而後咱們在List.cshtml中用foreach循環來列舉書本信息,代碼以下:

@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>     
}

最後咱們還須要修改一下默認路由,讓系統運行後直接導向到咱們的{controller = "Book", action = "List"},打開Global.asax文件,找到RegisterRoutes方法,進行以下修改:

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 }
    );
}

到這,咱們的程序能夠運行了,效果以下:

 

結束語:

本文是Ninject在ASP.NET MVC中使用的一個簡單示例,目的是讓你們瞭解Ninject在MVC中的使用方法。固然,Ninject的強大之處不只限於本文所演示的,相信當你熟悉了Niject以後,在搭建MVC應用程序時,你必定會喜歡上它的。

評論精選

#8樓 [ 樓主2013-11-02 08:23 |  Liam Wang 
@ heren2013
引用public BookController(IBookRepository bookRepository) {
repository = bookRepository;
}
請問在哪裏調用了這個構造函數,且構造函數中的參數在哪裏初始化呢,難道ninjectKernel.Bind<IBookRepository>().To<BookRepository>();這段代碼就已經初始花了構造函數裏的參數了嗎?求解

BookController類的建立(含初始化)主要通過下面這三個過程:
1.在Application_Start中,ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());這段註冊代碼告訴MVC用NinjectControllerFactory工廠類來建立全部Controller對象。
在NinjectControllerFactory類中包含了下面兩個過程:綁定接口到接口的實現和建立Controller類對象。
2.ninjectKernel.Bind<IBookRepository>().To<BookRepository>();這段綁定代碼告訴ninjectKernel當被請求一個IBookRepository接口的實現時則返回一個BookRepository對象。
3.請你閱讀NinjectControllerFactory類中的GetControllerInstance方法,經過ninjectKernel.Get(controllerType)這句代碼,ninject獲取controller(如BookController)對象的信息並建立該controller的實例,這個過程會調用controller的構造函數,它會自動判斷構造函數所須要的參數,如BookController類的構造函數須要一個IBookRepository接口參數,根據第2個過程ninject註冊的綁定,ninject會給該構造函數傳遞BookRepository對象(IBookRepository接口的實現者)的引用。

#19樓   2013-11-28 09:57 |  彼年豆蔻
@ Liam Wang
不須要啊,只須要在Application_Start()函數中註冊一下:
DependencyResolver.SetResolver(new Code.NinjectDependencyResolver());//註冊Ioc容器
而後在具體使用中,Controller構造注入或者使用屬性注入便可
相關代碼:
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<IUserService>().To<UserService>();
            _kernel.Bind<IRoleService>().To<RoleService>();
        }
        public object GetService(Type serviceType)
        {
            return _kernel.TryGet(serviceType);
        }
        public IEnumerable<object> GetServices(Type serviceType)
        {
            return _kernel.GetAll(serviceType);
        }
    }
}
protected void Application_Start()
{
    DependencyResolver.SetResolver(new Code.NinjectDependencyResolver());//註冊Ioc容器
    AreaRegistration.RegisterAllAreas();
    WebApiConfig.Register(GlobalConfiguration.Configuration);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);
}

public class UserController : Controller
{
    private readonly IUserService _user;
    public UserController(IUserService userService)
    {
        _user = userService;
    }
    public ActionResult Index()
    {
        return View();
    }
    public ActionResult GetAllUsers()
    {
        var pageIndex = Request["page"] == null ? 1 : int.Parse(Request["page"]);
        var pageSize = Request["rows"] == null ? 1 : int.Parse(Request["rows"]);
        int total;
        var data = _user.Query(pageIndex, pageSize, out total, u => true, true, u => u.Id);

        var result = new {total, rows = data};

        return Json(result, JsonRequestBehavior.AllowGet);
    }
}
 

 


 

 

參考:《Pro ASP.NET MVC 4》

相關文章
相關標籤/搜索