zoukankan      html  css  js  c++  java
  • IOC框架Ninject实践总结

    一、控制反转和依赖注入

    Ninject是一个轻量级的基于.Net平台的依赖注入(IOC)框架。所谓的IOC,即控制反转(Inversion of Control),它是一个经典的面向对象编程法则,它的作用主要是用来帮助应用程序解耦,并把程序分离成一个个松耦合高内聚的模块。控制反转还有一个名字叫依赖注入(Dependency Injection),简称DI。

     

    二、快速无xml配置注入

    1、定义应用程序Module

    using LogService;
    using LogService.Impl;
    using Ninject.Modules;
    using NinjectApp.Warrior;
    using NinjectApp.Weapon;
    
    namespace NinjectApp
    {
        internal class ServiceModule : NinjectModule
        {
            public override void Load()
            {
                Bind<ILogService>().To<DbLogService>();
    
                Bind<IWeapon>().To<Sword>().InSingletonScope();
                //Bind<IWeapon>().To<Shuriken>();
    
                Bind<Shuriken>().ToSelf().WhenInjectedInto<IWeapon>();
    
                Bind<IWarrior>().To<FootSoldier>();
                //Bind<IWarrior>().To<Samurai>();
    
            }
        }
    }

    2、手动调用服务

            /// <summary>
            /// 手动注入
            /// </summary>
            static void InjectManual()
            {
                using (var kernel = new StandardKernel(module))
                {
                    var dbLogger = kernel.Get<ILogService>();
                    dbLogger.AppendLog("hello world");
    
                    var weapon = kernel.Get<IWeapon>();
                    Console.WriteLine(weapon.GetType());
                    Console.WriteLine(weapon.Name);
    
                    //weapon = kernel.Get<Shuriken>();
                    //Console.WriteLine(weapon.GetType());
                    //Console.WriteLine(weapon.Name);
    
                    var weapon1 = kernel.Get<IWeapon>();
                    Console.WriteLine(weapon1.GetType());
                    Console.WriteLine(weapon1.Name);
    
                    Console.WriteLine(object.ReferenceEquals(weapon, weapon1));
    
                    var warrior = kernel.Get<IWarrior>();
                    Console.WriteLine(warrior.GetType());
                }
    
            }
    
    

     

    注:Ninject的绑定对象作用域有多种,本文的demo中有具体的单元测试,具体可以直接查看源码或者参考官方文档。。

     

    三、配置文件注入

    通过Ninject的xml扩展,可以实现传统的类似于Spring.net、Unity等IOC容器的注入方式。

    1、配置文件

    复制代码
    <?xml version="1.0" encoding="utf-8" ?>
    <module name="ServiceModule">
      <bind name="Txtlog" service="LogService.ILogService,LogService" to="LogService.Impl.TxtLogService,LogService"/>
      <!--<bind name="Dblog" service="LogService.ILogService,LogService" to="LogService.Impl.DbLogService,LogService"/>-->
      <bind name="Sword" service="NinjectApp.Weapon.IWeapon,NinjectApp" to="NinjectApp.Weapon.Sword,NinjectApp"/>
      <bind name="FootSoldier" service="NinjectApp.Warrior.IWarrior,NinjectApp" to="NinjectApp.Warrior.FootSoldier,NinjectApp"/>
    </module>
    
    
    
    复制代码

    2、利用扩展加载服务

    using System.Collections.Generic;
    using System.Xml.Linq;
    using Ninject;
    using Ninject.Extensions.Xml;
    using Ninject.Extensions.Xml.Handlers;
    
    namespace NinjectApp
    {
        public class XmlModuleContext
        {
            protected readonly IKernel kernel;
            protected readonly IDictionary<string, IXmlElementHandler> elementHandlers;
    
            public XmlModuleContext()
            {
                kernel = new StandardKernel();
                elementHandlers = new Dictionary<string, IXmlElementHandler> { { "bind", new BindElementHandler(kernel) } };
            }
        }
    
        public class XmlServiceModule : XmlModuleContext
        {
            private static readonly XmlServiceModule instance = new XmlServiceModule();
    
            protected readonly XmlModule module = null;
    
            public XmlServiceModule()
            {
                var document = XDocument.Load("Config/NinjectServiceModule.config");
                module = new XmlModule(document.Element("module"), elementHandlers);
                module.OnLoad(kernel);
            }
    
            public static IKernel GetKernel()
            {
                return instance.kernel;
            }
        }
    }

     

    3、调用服务

          /// <summary>
            /// 通过xml配置注入
            /// </summary>
            static void InjectByConfig()
            {
                var kernel = XmlServiceModule.GetKernel();
                var logger = kernel.Get<ILogService>();
                Console.WriteLine(logger.GetType());
                logger.AppendLog("hello world");
               
                var weapon = kernel.Get<IWeapon>();
                Console.WriteLine(weapon.GetType());
                Console.WriteLine(weapon.Name);
    
                var warrior = kernel.Get<IWarrior>();
                Console.WriteLine(warrior.GetType());
            }
    

     

    虽然配置注入看上去更容易扩展应对外部变化,但是项目庞大臃肿之后,配置文件并不好管理。固然有一些可视化的工具,但是仍然容易出现偏差。Ninject最擅长的基本注入功能就是无配置简单快速注入,达到free yourself from xml的目的,对于一般的中小型应用程序完全可以零配置。

     

    四、MVC项目的依赖注入

    通过Ninject的MVC扩展可以轻松实现MVC项目的依赖注入。

    1、NinjectHttpApplication

    在Global.asax.cs文件里重新定义MvcApplication继承自NinjectHttpApplication,重写OnApplicationStarted事件和CreateKernel方法:

    
            protected override void OnApplicationStarted()
            {
                base.OnApplicationStarted();
                AreaRegistration.RegisterAllAreas();
                RegisterGlobalFilters(GlobalFilters.Filters);
                RegisterRoutes(RouteTable.Routes);
            }
    
            protected override IKernel CreateKernel()
            {
                var kernel = new StandardKernel();
                kernel.Bind<IUserService>().To<UserInfoService>();
                kernel.Bind<ILogService>().To<TxtLogService>();
                //kernel.Bind<ILogService>().To<TxtLogService>().InSingletonScope();//单例
                return kernel;
            }
    

     

    2、通过构造函数或者属性或者Module实现注入

    (1)、Controller实现注入

    a、构造函数注入

            private readonly IUserService userService = null;
    
            public AccountController(IUserService userService)
            {
                this.userService = userService;
                //var userInfo = userService.GetCurrentUser();//do sth
            }
    

    b、属性注入

    定义属性,加上Inject特性即可实现注入。

           [Inject]
            public IUserService CurrentUserService { get; set; }
    
    

    c、module注入

    定义Module:

    using LogService;
    using LogService.Impl;
    using Ninject.Modules;
    using UserService;
    using UserService.Impl;
    
    namespace MVCApp.Helper
    {
        internal class ServiceModule : NinjectModule
        {
            public override void Load()
            {
                Bind<ILogService>().To<TxtLogService>();
                Bind<IUserService>().To<UserInfoService>();
            }
        }
    }
    

     

    接着调用即可:

       using (var kernel = new StandardKernel(new ServiceModule()))
                    {
                        var logger = kernel.Get<ILogService>();
                    }
    

     

    (2)、自定义Attribute实现注入

    using System.Web.Mvc;
    using LogService;
    using Ninject;
    
    namespace MVCApp.Helper
    {
        /// <summary>
        /// 异常处理特性 
        /// </summary>
        public class ExceptionHandleAttribute : HandleErrorAttribute
        {
            [Inject]
            public ILogService Logger { get; set; }
    
            public override void OnException(ExceptionContext filterContext)
            {
                Logger.AppendLog(filterContext.Exception.ToString());//记录日志
            }
        }
    }
    

     

    和Controller非常相似,示例使用属性加上Inject特性的方式实现注入,其他注入方式略过。

    到这里,你应该已经可以看到,这可以算是web应用程序中非常干净利落的注入方式,简单的令人发指。

     

    五、组合还是继承

    通过IOC框架实现服务依赖注入本来不难,但是这里或多或少会牵扯到一个问题:注入服务调用是使用组合还是继承?

    举例来说,最基础的用户服务(或者日志服务),一般的web应用程序几乎每个控制器(或者页面)都或多或少和用户有关系。

    问题来了,不同的控制器或者页面要调用用户服务该怎么做?

    下面以MVC项目为例来说明一下通常的注入方法。

    如你所知,通常的做法,所谓组合优于继承(继承被认为是一种强耦合),我们只要在需要调用服务的控制器中定义一个服务变量或者属性,通过构造函数注入,类似下面这样:

          private readonly IUserService userService = null;
    
            public AccountController(IUserService userService)
            {
                this.userService = userService;
            }
    

     

    然后在对应的Action中就可以调用用户服务了。

    如果你的Controller很少,这种方式当然可以接受。但是,实际项目中控制器真的比较多的时候,有一些几乎每个控制器必然用到的公共服务,我们是不是不得不哼哧哼哧写很多构造函数实现依赖注入呢?

    到这里,你一定想到,是啊,都调用一样的服务,几乎都类似的代码,重构吧!

    最简单的方式,利用继承,集中在一个地方(通常就叫BaseController吧)写一次,

    using System.Web.Mvc;
    using LogService;
    using Ninject;
    using UserService;
    using UserService.Model;
    
    namespace MVCApp.Helper
    {
        [ExceptionHandle]
        public class BaseController : Controller
        {
            /// <summary>
            /// 构造函数
            /// </summary>
            public BaseController()
            {
                if (CurrentLogService==null)
                {
                    using (var kernel = new StandardKernel(new ServiceModule()))
                    {
                        var logger = kernel.Get<ILogService>();
                        logger.AppendLog("As you see,in constructor log service is not initilized by inject attribute.");
                    }
                }
            }
    
            protected UserInfo CurrentUser
            {
                get { return CurrentUserService.CurrentUser; }
            }
    
            #region 服务
    
            [Inject]
            public IUserService CurrentUserService { get; set; }
    
            [Inject]
            public ILogService CurrentLogService { get; set; }
    
            #endregion
        }
    }
    
    

    然后,在相应的Controller下this点服务属性名调用一下,多么优雅干净简洁。但是这种方式有一点需要特别需要注意,在Controller的构造函数里调用服务初始化一些数据可能不能让你那么随心所欲,因为在构造函数内,服务还没有初始化。

    如上代码所示,通过Inject特性实现服务注入,通过继承实现公共服务调用,不管哪种表现形式的应用程序都可以使用,有AOP和继承的世界看上去是多么美好啊。当然了,具体使用哪种方式好每个人肯定都有自己的看法,实际项目中,我们通常选择组合和继承相结合的方式,这样就可以兼顾两者的优点实现注入。

    最后还有几个困扰人的问题需要思考:如何划分服务?服务和服务之间是否应该依赖注入?如果仅仅是在表现层实现依赖注入,难道不觉得IOC的作用有点太酱油了吗?

     

    domo下载:NinjectApp

     

    参考:http://www.ninject.org/wiki.html

    https://github.com/ninject/

     http://ninject.codeplex.com/

    http://www.cnblogs.com/cnmaxu/archive/2011/10/25/2224375.html

    http://martinfowler.com/articles/injection.html

  • 相关阅读:
    httpcontext in asp.net unit test
    initialize or clean up your unittest within .net unit test
    Load a script file in sencha, supports both asynchronous and synchronous approaches
    classes system in sencha touch
    ASP.NET MVC got 405 error on HTTP DELETE request
    how to run demo city bars using sencha architect
    sencha touch mvc
    sencha touch json store
    sencha touch jsonp
    51Nod 1344:走格子(贪心)
  • 原文地址:https://www.cnblogs.com/Bike/p/3415732.html
Copyright © 2011-2022 走看看