[ASP.NET MVC 小牛之路]05 - 使用 Ninject实现依赖注入

在[ASP.NET MVC 小牛之路]系列上一篇文章(依赖注入(DI)和Ninject)的末尾提到了在ASP.NET MVC中使用Ninject要做的两件事情,续这篇文章之后,本文将用一个实际的示例来演示Ninject在ASP.NET MVC中的应用。

为了更好的理解和撑握本文内容,强烈建议初学者阅读本文前先阅读依赖注入(DI)和Ninject

本文目录:

准备工作

新建一个名为BookShop的空白解决方案。在该解决方案中分别添加一个名为BookShop.WebUI的MVC空应用程序,和一个名为BookShop.Domain的类库工程。目录结构如下:

两个工程添加完后,在BookShop.WebUI工程下添加BookShop.Domain工程的引用。

使用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的工厂类,代码如下:

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内部会自动处理所有的依赖关系,智能地创建我们需要的对象。

Controller工厂类创建好后,我们就需要告诉MVC用我们的NinjectControllerFactory类来创建Controller对象,为此,需在Global.asax文件的Application_Start方法中添加下面代码:

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

    //设置Controller工厂
    ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
}

这里我们暂且不去关心上面这段代码是什么原理,知道设置自定义的Controller工厂必须要在这注册就行了,有空的话我会在后续博文对这部分内容进行更深入的讲解。

添加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);
    }
}
时间: 2024-12-20 00:58:27

[ASP.NET MVC 小牛之路]05 - 使用 Ninject实现依赖注入的相关文章

[ASP.NET MVC 小牛之路]18 - Web API

原文:[ASP.NET MVC 小牛之路]18 - Web API Web API 是ASP.NET平台新加的一个特性,它可以简单快速地创建Web服务为HTTP客户端提供API.Web API 使用的基础库是和一般的MVC框架一样的,但Web API并不是MVC框架的一部分,微软把Web API相关的类从 System.Web.Mvc 命名空间下提取了出来放在 System.Web.Http 命名空间下.这种理念是把 Web API 作为ASP.NET 平台的核心之一,以使Web API能使用在

[ASP.Net] 转 &gt; ASP.NET MVC 小牛之路

URL: http://www.cnblogs.com/willick/ 看到了不错的学习笔记,MVC.Net学习之路展开 [ASP.NET MVC 小牛之路]18 - Web API [ASP.NET MVC 小牛之路]17 - 捆绑(Bundle) [ASP.NET MVC 小牛之路]16 - Model 验证 [ASP.NET MVC 小牛之路]15 - Model Binding [ASP.NET MVC 小牛之路]14 - Unobtrusive Ajax [ASP.NET MVC 小

[ASP.NET MVC 小牛之路]03 - Razor语法(转)

出处:http://www.cnblogs.com/willick/p/3224144.html Razor是MVC3中才有的新的视图引擎.我们知道,在ASP.NET中,ASPX的视图引擎依靠<%和%>来调用C#指令.而MVC3以后有了一套新的使用@标记的Razor语法,使用起来更灵活更简洁.下面通过一些简单示例让大家快速撑握Razor语法的使用. 本文目录 准备工作 在演示Razor语法的使用之前,我们需要做一些准备工作. 1.打开VS创建一个ASP.NET MVC空项目,很简单,就不具体演

[ASP.NET MVC 小牛之路]01 - 理解MVC模式--转载

PS:MVC出来很久了,工作上一直没机会用.出于兴趣,工作之余我将展开对MVC的深入学习,通过博文来记录所学所得,并希望能得到各位园友的斧正. 本文目录 理解一般意义上的MVC模式 MVC模式(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为以下三个基本部分: 模型(Model):模型用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法.“模型”有对数据直接访问的权力,例如对数据库的访问.“模型”不依赖“视图”和“控制器”,也就是说,模型不关心它会

[ASP.NET MVC 小牛之路]02 - C#知识点提要--转载

本篇博文主要对asp.net mvc开发需要撑握的C#语言知识点进行简单回顾,尤其是C# 3.0才有的一些C#语言特性.对于正在学asp.net mvc的童鞋,不防花个几分钟浏览一下.本文要回顾的C#知识点有:特性.自动属性.对象集合初始化器.扩展方法.Lambda表达式和Linq查询.C#资深“玩家”可路过. 本文目录 1.特性(Attributes) 特性(Attributes),MSDN的定义是:公共语言运行时允许你添加类似关键字的描述声明,叫做attributes, 它对程序中的元素进行

[ASP.NET MVC 小牛之路]03 - Razor语法--转载

Razor是MVC3中才有的新的视图引擎.我们知道,在ASP.NET中,ASPX的视图引擎依靠<%和%>来调用C#指令.而MVC3以后有了一套新的使用@标记的Razor语法,使用起来更灵活更简洁.下面通过一些简单示例让大家快速撑握Razor语法的使用. 本文目录 准备工作 在演示Razor语法的使用之前,我们需要做一些准备工作. 1.打开VS创建一个ASP.NET MVC空项目,很简单,就不具体演示了. 2.添加一个Model.在项目的Models文件夹中添加一个名为Product的类.在这我

[ASP.NET MVC 大牛之路]02 - C#高级知识点概要(1) - 委托和事件

在ASP.NET MVC 小牛之路系列中,前面用了一篇文章提了一下C#的一些知识点.照此,ASP.NET MVC 大牛之路系列也先给大家普及一下C#.NET中的高级知识点.每个知识点不太会过于详细,但足矣.要深入研究还需要去查看更多的专业资料. 要成为大牛,必然要有扎实的基本功,不然时间再长项目再多也很难有大的提升.本系列讲的C# 高级知识点,是非常值得去撑握的,不仅可以让你写代码时游刃有余,而且去研究和学习一些开源项目时,也不会显得那么吃力了. 希望大家记住,这里讲的所有的知识点,不仅仅是了解

[ASP.NET MVC 大牛之路]01 - 开篇

匆匆2014,转眼就到末尾了.在这一年,你还有哪事情些想做而没有做? 2014年在我身上发生了两件意义重大的事,一是正月初一宝宝出生,我升级成为了爸爸:二是加入了瞬聘网,成为了技术负责人. 2014我计划想做的事,比如带家人去一次旅行.看完家里那几本想看而没看的书.写完[ASP.NET MVC 大牛之路]系列博客.做一个教育类的网站等等,计划其实都还算合理,但终究还是80%的事没有完成,50%的事根本没有去做. 去年结束了[ASP.NET MVC 小牛之路]系列,本来计划在2014年接着写大牛之

[ASP.NET MVC 大牛之路]03 - C#高级知识点概要(2) - 线程和并发

我也想过跳过C#高级知识点概要直接讲MVC,但经过前思后想,还是觉得有必要讲的.我希望通过自己的经验给大家一些指引,带着大家一起走上ASP.NET MVC大牛之路,少走弯路.同时也希望能和大家一起交流,这样也能发现我自己的不足,对我自己的帮助也是非常大的. 建议大家对C#撑握的不错的时候,可以去看一些开源项目.走技术这条路,就要耐得住寂寞(群里双休日说要让群主找妹子进群的人必须反思),练好内功.不撑握C#高级知识点,别想看懂优秀的开源项目,更别指望吸收其编程思想:你的水平,随时可以被一个实习生代