本文转载自kuangkro 如果给您带来不便请联系博主

一、控制反转和依赖注入

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的作用有点太酱油了吗?

参考: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

Ninject学习笔记<二>的更多相关文章

  1. Ninject学习笔记<四>

    前言 前段时间看Mvc最佳实践时,认识了一个轻量级的IOC框架:Ninject.通过google搜索发现它是一个开源项目,最新源代码地址是:http://github.com/enkari/ninje ...

  2. C#可扩展编程之MEF学习笔记(四):见证奇迹的时刻

    前面三篇讲了MEF的基础和基本到导入导出方法,下面就是见证MEF真正魅力所在的时刻.如果没有看过前面的文章,请到我的博客首页查看. 前面我们都是在一个项目中写了一个类来测试的,但实际开发中,我们往往要 ...

  3. IOS学习笔记(四)之UITextField和UITextView控件学习

    IOS学习笔记(四)之UITextField和UITextView控件学习(博客地址:http://blog.csdn.net/developer_jiangqq) Author:hmjiangqq ...

  4. java之jvm学习笔记四(安全管理器)

    java之jvm学习笔记四(安全管理器) 前面已经简述了java的安全模型的两个组成部分(类装载器,class文件校验器),接下来学习的是java安全模型的另外一个重要组成部分安全管理器. 安全管理器 ...

  5. Learning ROS for Robotics Programming Second Edition学习笔记(四) indigo devices

    中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ...

  6. Typescript 学习笔记四:回忆ES5 中的类

    中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...

  7. ES6学习笔记<四> default、rest、Multi-line Strings

    default 参数默认值 在实际开发 有时需要给一些参数默认值. 在ES6之前一般都这么处理参数默认值 function add(val_1,val_2){ val_1 = val_1 || 10; ...

  8. muduo网络库学习笔记(四) 通过eventfd实现的事件通知机制

    目录 muduo网络库学习笔记(四) 通过eventfd实现的事件通知机制 eventfd的使用 eventfd系统函数 使用示例 EventLoop对eventfd的封装 工作时序 runInLoo ...

  9. python3.4学习笔记(四) 3.x和2.x的区别,持续更新

    python3.4学习笔记(四) 3.x和2.x的区别 在2.x中:print html,3.x中必须改成:print(html) import urllib2ImportError: No modu ...

  10. Go语言学习笔记四: 运算符

    Go语言学习笔记四: 运算符 这章知识好无聊呀,本来想跨过去,但没准有初学者要学,还是写写吧. 运算符种类 与你预期的一样,Go的特点就是啥都有,爱用哪个用哪个,所以市面上的运算符基本都有. 算术运算 ...

随机推荐

  1. 软工实践练习——使用git进行代码管理心得

    一.在Github上注册账户.其中创建organization在小组成员的账户上创建,并在其账户上创建了小组的版本库.在创建organization的过程中,参考了助教提供的博客:http://sef ...

  2. AtomicInteger源码分析

    问题背景 最近在看LinkedBlockingQueue看到了其中的count使用AtomicInteger修饰,之前也看过AtomicInteger的一些解释,也是似懂非懂的,今天深入的了解了其实现 ...

  3. struts1日期转换处理

    问题场景 最近在维护公司旧的系统(用的struts1框架)的时候,在日期处理的时候,我将日期设定为Date类型,结果报以下错误: javax.servlet.ServletException: Bea ...

  4. MVC学习Day02之校验

    MVC校验有两种方法: 方法一:自己动手写js---------略 方法二: l在View的页面中,首先指定页面强类型@model 类型 l使用Html.***For(model=>model. ...

  5. 安装Eclipse插件

    安装Eclipse插件   从eclipse 3.6开始,eclipse有一个marketplace,这个类似现在手机的app store一样,可以在其中检索相关插件,直接安装,打开help--> ...

  6. visual studio各个版本的差异

  7. SpringMVC 2.5.6 noMapping

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  8. android 事件处理概念簇

    1)事件传递函数 2)事件相关: 事件.事件源.事件监听器.MotionEvent 3)坐标系.定位.路由: 4)Window activity view viewGroup

  9. C/C+小记

    1.struct与typedef struct struct Student{int a;int b}stu1; //定义名为Student的结构体,及一个Student变量stu1 struct { ...

  10. LWP 轻量级线程的意义与实现

    转子 http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/ 二.Linux 2.4内核中的轻量进程实现 最初的进程定义都包含程序.资源 ...