在MVC3项目中结合NInject组件实现依赖注入的设计

这是本次MVC3讲座中的一个话题,整理出来给大家参考参考

名词解释

依赖注入:英文是Dependency Injection。有时候也称为反转控制(Ioc)吧。不管名词怎么讲,它的大致意思是,让我们的应用程序所依赖的一些外部服务,可以根据需要动态注入,而不是预先在应用程序中明确地约束。这种思想,在当前的软件开发领域,为了保证架构的灵活性,应该还是很有意义的。

在MVC这个框架中,为依赖注入的设计提供了先天的支持。结合一些我们熟知的DI组件,例如NInject,我们可以较为容易地实现上述提到的功能。

场景介绍

我们的应用程序,需要支持各种不同的数据源,而且我们希望日后可以很容易地切换,不会因为数据源的变化而导致对Contoller或者Model,或者View做修改。

本文完整源代码,请通过这里下载 MvcApplicationDISample.rar

演练步骤

第一步:准备一个MVC项目(选择空白模板)

image

第二步:准备一个业务实体类型

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace MvcApplicationDISample.Models
{
    public class Employee
    {
        public int ID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
}

第三步:准备一个数据访问的接口定义

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using MvcApplicationDISample.Models;

namespace MvcApplicationDISample.Services
{
    public interface IDataService
    {
        Employee[] GetEmployee();
    }
}

第四步:创建一个HomeController

image

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcApplicationDISample.Services;
using MvcApplicationDISample.Models;


namespace MvcApplicationDISample.Controllers
{
    public class HomeController : Controller
    {

        IDataService DataService;
        public HomeController(IDataService service)
        {
            DataService = service;
        }

        //
        // GET: /Home/

        public ActionResult Index()
        {
            var data = DataService.GetEmployee();
            return View(data);
        }

    }
}

注意,这里需要为HomeController添加一个特殊的构造函数,传入IDataService这个接口。通常,所有的DI组件都是通过这样的方式注入的。

在设计HomeController的时候,我们不需要关心到底日后会用具体的哪种DataService,我们只是要求要传入一个IDataService的具体实现就可以了,这就是DI的本质了。

到这里为止,我们该做的准备工作基本就绪了。下面来看看如何结合DI组件来实现我们的需求

第五步:引入NInject组件

这是我比较喜欢的一个DI组件。它还针对MVC3专门有一个扩展

image

image

添加这个组件之后,除了自动添加了很多引用之外,还有一个特殊的文件App_Start\NinjectMVC3.cs

image

[assembly: WebActivator.PreApplicationStartMethod(typeof(MvcApplicationDISample.App_Start.NinjectMVC3), "Start")]
[assembly: WebActivator.ApplicationShutdownMethodAttribute(typeof(MvcApplicationDISample.App_Start.NinjectMVC3), "Stop")]

namespace MvcApplicationDISample.App_Start
{
    using System.Reflection;
    using Microsoft.Web.Infrastructure.DynamicModuleHelper;
    using Ninject;
    using Ninject.Web.Mvc;

    public static class NinjectMVC3 
    {
        private static readonly Bootstrapper bootstrapper = new Bootstrapper();

        /// <summary>
        /// Starts the application
        /// </summary>
        public static void Start() 
        {
            DynamicModuleUtility.RegisterModule(typeof(OnePerRequestModule));
            DynamicModuleUtility.RegisterModule(typeof(HttpApplicationInitializationModule));
            bootstrapper.Initialize(CreateKernel);
        }
        
        /// <summary>
        /// Stops the application.
        /// </summary>
        public static void Stop()
        {
            bootstrapper.ShutDown();
        }
        
        /// <summary>
        /// Creates the kernel that will manage your application.
        /// </summary>
        /// <returns>The created kernel.</returns>
        private static IKernel CreateKernel()
        {
            var kernel = new StandardKernel();
            RegisterServices(kernel);
            return kernel;
        }

        /// <summary>
        /// Load your modules or register your services here!
        /// </summary>
        /// <param name="kernel">The kernel.</param>
        private static void RegisterServices(IKernel kernel)
        {
        }        
    }
}

这个类型很有意思,WebActivator.PreApplicationStartMethod这个方法其实是注册了一个在MVC程序启动之前运行的方法。这些代码大家应该能看懂,它在CreateKernel中,添加一个新的Kernel(用来做注入的容器)。

第六步:创建一个IDataService的具体实现

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MvcApplicationDISample.Models;

namespace MvcApplicationDISample.Services
{
    public class SampleDataService:IDataService
    {
        #region IDataService Members

        public Employee[] GetEmployee()
        {
            return new[]{
                new Employee(){ID=1,FirstName="ares",LastName="chen"}};
        }

        #endregion
    }
}

作为举例,我们这里用了一个硬编码的方式实现了该服务。

第七步:实现注入

回到App_Start\NinjectMVC3.cs这个文件,修改RegisterServices方法如下

       /// <summary>
        /// Load your modules or register your services here!
        /// </summary>
        /// <param name="kernel">The kernel.</param>
        private static void RegisterServices(IKernel kernel)
        {
            kernel.Bind<Services.IDataService>().To<Services.SampleDataService>();
        }      

第八步:测试Controller的功能

image

我们可以看到,数据已经展现出来了。这说明,HomeController中的Index方法,确实调用了我们后期插入的这个SampleDataService。而通过下图,则可以更加清楚看到这一点

image

到这里为止,我们就结合Ninject组件实现了一个简单的依赖注入的实例。Ninject 针对MVC 3有这么一个特殊的文件,可以极大地方便我们的编程。但即便没有这个文件,我们也可以通过另外一些方法来实现需求。

下面介绍两种比较传统的,通过扩展MVC组件实现的方式

第一种:实现自定义ControllerFactory

我们都知道,Controller其实都是由ControllerFactory来生成的,那么,为了给所有新创建从Controller都自动注入我们的服务,那么就可以从ControllerFactory这个地方动动脑筋了。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Ninject;
using MvcApplicationDISample.Services;

namespace MvcApplicationDISample.Extensions
{
    public class InjectControllerFactory:DefaultControllerFactory
    {

        private IKernel kernel;
        public InjectControllerFactory()
        {
            kernel = new StandardKernel();
            kernel.Bind<IDataService>().To<SampleDataService>();
        }
        protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
        {
            return (IController)kernel.Get(controllerType);
        }
    }
}

要使用这个自定义的 ControllerFactory,我们需要修改Global.ascx文件中的Application_Start方法,添加下面的粗体部分代码

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);

           ControllerBuilder.Current.SetControllerFactory(new Extensions.InjectControllerFactory());         }

这样做好之后,我们可以测试HomeController中的Index这个Action,我们发现它还是能正常工作。

image

第二种:实现自定义的DependencyResolver

顾名思义,这就是MVC框架里面专门来处理所谓的依赖项的处理器。可以说这是MVC专门为DI准备的一个后门。下面是我写好的一个例子

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Ninject;
using MvcApplicationDISample.Services;

namespace MvcApplicationDISample.Extensions
{
    public class InjectDependencyResolver:IDependencyResolver
    {
        private IKernel kernel;

        public InjectDependencyResolver()
        {
            kernel = new StandardKernel();
            kernel.Bind<IDataService>().To<SampleDataService>();
        }

        #region IDependencyResolver Members

        public object GetService(Type serviceType)
        {
            return kernel.TryGet(serviceType);
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return kernel.GetAll(serviceType);
        }

        #endregion
    }
}
 

那么,如何使用这个自定义的处理器呢?

很简单,我们仍然是修改Global.asax文件中的Application_Start方法

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);

            //ControllerBuilder.Current.SetControllerFactory(new Extensions.InjectControllerFactory());

            DependencyResolver.SetResolver(new Extensions.InjectDependencyResolver());
        }



请注意,之前那个设置ControllerFactory的代码,我们可以注释掉了

这个解决方案的最终效果和之前是一样的。

image

本文完整源代码,请通过这里下载 MvcApplicationDISample.rar

陈希章@中国

:: 心有多宽,路就有多宽 :: | :: http://tech.xizhang.com :: Microsoft .NET开发人员职业规划和学习路线图:: | ::架构师访谈

ASP.NET MVC

这里保存与ASP.NET MVC有关的项目实践及笔记

MVC3课程中的几个问题整理
摘要: 这是几个小问题,整理出来给大家参考 1. 如何为不同类型的属性设置不同的编辑界面 备注:这个实例的源代码,可以通过这里下载 MvcApplicationEditTemplate.rar 我们探讨到...阅读全文

posted @ 2011-12-13 11:27 陈希章 阅读(563) | 评论 (5) 编辑

在MVC3项目中结合NInject组件实现依赖注入的设计
摘要: 这是本次MVC3讲座中的一个话题,整理出来给大家参考参考 名词解释 依赖注入:英文是Dependency Injection。有时候也称为反转控制(Ioc)吧。不管名词怎么讲,它的大致意思是,让我们的...阅读全文

posted @ 2011-12-12 21:19 陈希章 阅读(777) | 评论 (7) 编辑

在MVC项目中为用户登录失败次数实现提示
摘要: 这两天在给一个客户讲解MVC 3的架构和在项目中的应用,有提到这样一个问题: MVC3 默认实现的Forms Authentication机制,可以结合SQL Server做成员管理,而且可以设置在...阅读全文

posted @ 2011-12-11 21:27 陈希章 阅读(893) | 评论 (2) 编辑

ASP.NET自定义身份验证的实践
摘要: 事情是这样的:有一套ASP.NET应用程序,用户一般会用工号登录,但是我们希望在应用程序中方便地访问到该用户相关的其他信息,例如用户名或者工厂名称。 按照标准的ASP.NET Membership的做...阅读全文

posted @ 2010-11-06 07:06 陈希章 阅读(2110) | 评论 (16) 编辑

MVC 2中HandleErrorFilter的问题及其解决方法
摘要: 近日在使用MVC2的异常处理的时候,遇到一些问题 1. 我们的Error.aspx是没有使用MasterPage 2.通过如下代码测试我们发现会遇到500错误通过工具,我看到下面代码其实要讲起来,应该...阅读全文

posted @ 2010-10-23 08:05 陈希章 阅读(1261) | 评论 (12) 编辑

在MVC项目中如何显示图片
摘要: 首先,有好一阵没有怎么写博客文章了.实在也是很多事情,确实没有停下来过. 这两天在讲解MVC方面的知识和项目实践,其中有一个小的细节,是有关于图片显示方面的,记录下来供大家参考 在MVC项目中,要显示...阅读全文

posted @ 2010-09-12 19:22 陈希章 阅读(2638) | 评论 (12) 编辑

T4 Templates: A Quick-Start Guide for ASP.NET MVC Developers
摘要: 本文链接:http://blogs.msdn.com/webdevtools/archive/2009/01/29/t4-templates-a-quick-start-guide-for-asp-n...阅读全文

posted @ 2010-03-03 09:51 陈希章 阅读(274) | 评论 (0) 编辑

ASP.NET MVC : UrlHelper
摘要: http://msdn.microsoft.com/en-us/library/system.web.mvc.urlhelper.action.aspx UrlHelper.Action Method...阅读全文

posted @ 2010-02-05 10:50 陈希章 阅读(222) | 评论 (0) 编辑

AJAX,JSON与MVC
摘要: 有几个特殊之处 1. MVC框架中包含了一个特殊的JSONActionResult,可以直接返回JSON对象,注意它的格式与之前的asmx和页面静态方法都不一样,它直接就是一个JSON对象 2. 服务...阅读全文

posted @ 2010-01-31 21:23 陈希章 阅读(467) | 评论 (1) 编辑

ASP.NET MVC 2中的数据验证
摘要: 对照scottgu的博客,我试用了一下这个新增的数据验证功能,总的来说,还是比较方便的。我简单地总结步骤如下 1. 添加引用 2. 修改业务实体类,在需要进行验证的Property上面添加一些特殊的...阅读全文

posted @ 2010-01-24 18:41 陈希章 阅读(730) | 评论 (9) 编辑

ASP.NET MVC 2
摘要: 关于MVC2,我之前也有些介绍,现在也正在使用这个版本了。下面这个站点是Scottgu的博客 http://weblogs.asp.net/scottgu/archive/2010/01/10/asp...阅读全文

posted @ 2010-01-24 17:11 陈希章 阅读(260) | 评论 (0) 编辑

MVC:如何使用站点地图
摘要: 之前在Web Form这种模型下面,我们可以很方便地使用站点地图进行导航。那么这个功能在MVC中是否可以同样的实现呢?答案是默认不可以。 但有一个开源的项目,可以解决这个问题 http://mvcsi...阅读全文

posted @ 2010-01-22 21:59 陈希章 阅读(272) | 评论 (0) 编辑

MVC:如何设计多语言支持
摘要: ASP.NET MVC是否继续支持多语言呢?答案是肯定的。全局资源文件和本地资源文件都是支持的阅读全文

posted @ 2010-01-22 21:50 陈希章 阅读(180) | 评论 (0) 编辑

Uninstalling ASP.NET MVC 1.1 after installing Visual Studio 2010 beta 2
摘要: http://www.garrardkitchen.com/2009/10/uninstalling-asp-net-mvc-1-1-after-installing-visual-studio-20...阅读全文

posted @ 2009-12-29 19:53 陈希章 阅读(115) | 评论 (0) 编辑

使用MVC框架中要注意的问题(八):HandleError
摘要: 在ASP.NET MVC中,我们可以使用HandleErrorAttribute特性来具体指定如何处理Action抛出的异常.只要某个Action设置了 HandleErrorAttribute特性,...阅读全文

posted @ 2009-12-29 17:22 陈希章 阅读(1110) | 评论 (1) 编辑

使用MVC框架中要注意的问题(七):HtmlAttributes
摘要: 在MVC的View中,我们可以通过HtmlHelper的一些扩展方法插入一些控件,例如通过Html.TextBox插入一个文本框等等,下面是一个简单的范例<%@ Page Title="" La...阅读全文

posted @ 2009-12-29 15:08 陈希章 阅读(727) | 评论 (2) 编辑

使用MVC框架中要注意的问题(六):何时使用PartialView方法
摘要: 我们知道,作为Action的响应,最常见的做法是Return View();也就是说,返回一个视图。但是如果我们某的操作只是要返回页面的一部分,典型的情况就是,在页面上实现局部的刷新功能。 实现局部刷...阅读全文

posted @ 2009-12-29 12:38 陈希章 阅读(1178) | 评论 (0) 编辑

使用MVC框架中要注意的问题(五):如何在页面和用户控件之间传递数据
摘要: 在MVC中,页面被称为View,而用户控件则被称为PartialView。如何在它们之间传递数据呢? 答案是: 默认情况下,PartialView能够访问到View里面的 ViewData. 如果页...阅读全文

posted @ 2009-12-29 12:03 陈希章 阅读(506) | 评论 (0) 编辑

使用MVC框架中要注意的问题(四):ActionLink只是执行Get的操作
摘要: ActionLink是产生一个链接字符串,它仅仅支持GET的Action<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Share...阅读全文

posted @ 2009-12-29 10:27 陈希章 阅读(369) | 评论 (0) 编辑

使用MVC框架中要注意的问题(三):如何为链接指定路径
摘要: 在视图中,我们会用到很多的a和img标签,它们只能接受相对路径。但因为在MVC框架中,视图都是放在不同的文件夹中,那么如何更好滴指定它们的路径呢? 一般的做法是通过.. 这样的方式来向上一级目录回退。...阅读全文

posted @ 2009-12-28 21:29 陈希章 阅读(297) | 评论 (0) 编辑

使用MVC框架中要注意的问题(二):将Model和Controller单独用一个项目设计
摘要: 这个问题很多朋友都会问到,MVC让分工协作成为了可能。但如果所有代码和页面都在一个项目中的话,那么分工就会受到限制。其实,Model和Controller都可以单独用一个(或者多个)程序单独来做。 1...阅读全文

posted @ 2009-12-27 20:45 陈希章 阅读(302) | 评论 (0) 编辑

使用MVC框架中要注意的问题(一):修改首页以支持主题
摘要: 下面的一个项目将使用MVC框架,陆续也可能会整理出来一些日记。今天说一下的是,如果你希望在MVC项目中使用主题(Apo_Themes),那么可能需要修改首页 我们先来看一下主题的设置 这样设置好了之后...阅读全文

posted @ 2009-12-27 18:31 陈希章 阅读(301) | 评论 (0) 编辑

ASP.NET MVC 实战演练
摘要: 关于MVC,已经有了很多的讨论。这一篇我用一个简单的实例演示了如何使用它,以及几个常见问题的解答。我推荐大家要了解一下MVC,尽可能地话,提前尝试用他做一些项目,这样理解会更加深刻 1. 添加一个C...阅读全文

posted @ 2009-12-27 12:52 陈希章 阅读(477) | 评论 (0) 编辑

原文地址:https://www.cnblogs.com/Leo_wl/p/2286248.html