在[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
下图说明安装成功:函数
咱们知道,在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
在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; } }
咱们知道,咱们确定须要一种方式来从数据库中读取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应用程序时,你必定会喜欢上它的。
@ 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》
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接口的实现者)的引用。