zoukankan      html  css  js  c++  java
  • 在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

  • 相关阅读:
    Spring 梳理-启用MVC
    Spring 梳理-MVC-配置DispatcherServet和ContextLoaderListener
    Spring 梳理-MVC-前端控制器DispatchServlet及URL请求处理过程
    Spring 梳理-AOP
    Spring 梳理-运行时动态注入bean
    Spring 梳理-bean作用域
    Spring 梳理-profile与条件化定义bean
    Spring 梳理-bean配置与装配
    Spring 梳理-容器(container)
    iframe
  • 原文地址:https://www.cnblogs.com/chenxizhang/p/2285304.html
Copyright © 2011-2022 走看看