http://www.bbsmvc.com/archiver/csharp/thread-831-1.html

本来想使用一下Ninject的,然后搜索了很久,都没找到比较详细的关于Ninject的使用方法等内容.于是乎干脆自己来写几篇介绍Ninject的内容.
1.      依赖注入和IOC
依赖注入和IOC的概念,可以点击这里看之前的文章.在这里就不多介绍了.
2.      环境准备
Ø  开发环境:WIN7 + VS2010 SP1
Ø  Ninject:本节内容以Ninject2.2.0.0-release-net-4.0为基础进行介绍。可以点击这里打开Ninject的官网进行下载。需要说明的是,CodePlex上虽然也有Ninject项目,但其基本没怎么更新。所以就不要从那里下载了。直接到Ninject其官方网站下载吧。
3.简单的例子
Ø  需求描述:
系统有任务消息,每个任务消息都有消息号,消息号的产生可以是以配置文件中的基数为基础产生,也可以是根据数据库中的某个字段产生。
Ø  步骤:
1).定义一个消息接口:

public interface IMessage
   {
       /// <summary>
       /// 取得消息号
       /// </summary>
       /// <returns></returns>
       string GetMsgNumber();
   }

2).从配置文件中获取消息号,实现消息接口。代码如下:

public class MessageFile : IMessage
   {
       public string GetMsgNumber()
       {
           return "从文件中获取消息号!";
       }
   }

3).从数据库中获取消息号,实现消息接口。代码如下:

public class MessageDB : IMessage
   {
       public string GetMsgNumber()
       {
           return "从数据中读取消息号";
       }
   }

4).下面就是准备在我们的应用中注册的接口和接口实现了。代码如下:

using Ninject.Modules;
public class MessageModule : NinjectModule
   {
       public override void Load()
       {
           //绑定接口并指定接口的实现。
           Bind<IMessage>().To<MessageDB>();
       }
   }

通过代码我们可以看到,我们只需要继承自NinjectModule并重载其Load()方法,在Load()方法中进行绑定接口并指定其具体实现即可。
5).接着,就是具体的使用了,代码如下:

using Ninject;
static void Main(string[] args)
       {
           using (var kernal = new StandardKernel(new MessageModule()))
           {
               var msg = kernal.Get<IMessage>();
               var msgNo = msg.GetMsgNumber();
               Console.WriteLine(msgNo);
               Console.Read();
           }
       }

运行代码(注意:在运行代码之前,请将项目的Framework选择中4.0版本,否则会“未能解析引用的程序集“Ninject”,因为它对不在当前目标框架“.NETFramework,Version=v4.0,Profile=Client”中的“System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a”具有依赖关系“异常出现),最后我们可以看到,控制台打出的消息是:

从数据中读取消息号!
 
在代码中,我们通过Kernal的Get方法来取得相应的类型实例,在这整个代码过程中,我们只是在Module中绑定了接口和实现,然后在其他地方都是通过接口类型来实现代码的。如何需要换成从配置文件中获取消息号,则只需要修改自定义的Module中的代码即可。毫无疑问,在程序的类型依赖和耦合方面都有很多优点。

4.接下来,介绍使用Ninject进行依赖注入的几种方式。
Ø  构造函数注入
首先,定义使用依赖接口初始化的类, 具体如下:

public class MessageCfg
   {
       private readonly IMessage msg;
       /// <summary>
       /// 通过接口构造
       /// </summary>
       /// <param name="msg"></param>
       public MessageCfg(IMessage msg)
       {
           this.msg = msg;
       }
 
       public string GetMsgNumber()
       {
           return this.msg.GetMsgNumber();
       }
   }

然后,我们的代码就可以这样写了:

using (var kernal = new StandardKernel(new MessageModule()))
           {
               var msg = kernal.Get<IMessage>();
               var msgcfg = new MessageCfg(msg);//构造函数注入
               Console.WriteLine(msgcfg.GetMsgNumber());
               Console.Read();
           }

这里,我们可以初始化MessageCfg的时候,只需要使用kernal.Get<IMessage>()获取的类型即可,而不用关心其具体是什么。
Ø  属性注入
首先定义使用属性注入的类,具体如下:

public class MessageCfg
   {
      //定义接口属性
       public IMessage Msg { get; set; }
       public MessageCfg() { }
       public string GetMsgNumber()
       {
           return this.Msg.GetMsgNumber();
       }
   }

具体使用代码如下:

using (var kernal = new StandardKernel(new MessageModule()))
           {
               //属性注入
               var msgcfg = new MessageCfg() { Msg = kernal.Get<IMessage>() };
               Console.WriteLine(msgcfg.GetMsgNumber());
               Console.Read();
           }

其他几种注入都大同小异,使用方式差不多。
最后,我们可以看到使用Ninject的基本步骤很简单。只需要定义接口à实现接口à继承NinjectModule在其Load()方法中指定相应的绑定接口和接口实现à通过自己的Module的来初始化Kernalà通过Get方法来获得相应的实现。

Ninject中提供多种接口和实现类的绑定的方式供我们选择,同时还可以设置相关的绑定项以及对象的作用域等。具体如下:(其中使用到的类和代码重用上一节的“Ninject 2.x细说---1.基本使用”中的定义)
1.        绑定:
Ninject中提供好几种绑定接口实现的方法,具体如下:

Ø To:绑定到接口的具体实现。

Ø ToConstant:绑定到某个常量值。
Ø ToMethod:绑定到方法。
Ø ToProvider:绑定到某个自定义的对象创建提供程序。
Ø ToSelf:自绑定。
代码如下:
1)        自绑定
Ninject可以使用ToSelf()方法,将自己绑定自己,这里必须是一个具体的类。

Bind<MessageCfg>().ToSelf();

2)        绑定到方法:

Bind<IMessage>().ToMethod(context => new MessageDB());

3)        绑定到具体的类型

Bind<IMessage>().ToConstant(new MessageDB());

4)        绑定到指定的服务提供程序

以及Bind<IMessage>().ToProvider(实现IProvider接口的服务提供程序);

2.        指定相关绑定信息:
Ninject中,可以在绑定的时候指定一些附加信息,以便更加明确构造函数或者是给某些属性赋值或者在绑定时回调方法。如下面的代码中:Bind<IMessage>().To<MessageDB>().WithConstructorArgument("msg", 1);
我们在绑定的同时指定了构造函数的参数以及值。
此外,还可以设置的指定信息分别有:

Ø WithConstructorArgument:指定构造函数中相关的参数,还有回调方法的重载。
Ø WithMetadata:指定相关元数据信息。
Ø WithParameter:指定相关的自定义参数。这个方法也实现构造函数参数值指定,与WithConstructorArgument类似,如:Bind<IMessage>().To<MessageDB>().WithConstructorArgument("msg", 1);同样可以写成:Bind<IMessage>().To<MessageDB>().WithParameter(newConstructorArgument("msg", 1));
Ø WithPropertyValue:指定相关的属性值,还有回调方法的重载。

3.        条件绑定:
Ninject中还可以指定相关的绑定的条件,只有条件满足的情况的下,才将相关的接口实现绑定到相关的接口上。如:

Bind<IMessage>().To<MessageDB>().WhenInjectedInto<MessageCfg>();

上面的代码,由于MessageCfg依赖与IMessage接口,所以其意思是在MessageCfg类中依赖的IMessage接口与MessageDB类绑定。
类似的还有When()如:

Bind<IMessage>().To<MessageDB>().When(cxt => cxt.Service.Name.StartsWith("Msg"));

其他的条件还有WhenClassHas、WhenParentNamed、WhenTargetHas等条件绑定。

 

4.        设置注入项
在Ninject中可以通过NinjectSettings类来指定注入项。如:

////设置注入项
var settings = new NinjectSettings() { AllowNullInjection = true };
using (var kernal = new StandardKernel(settings, new MessageModule()))
{
var msgcfg = kernal.Get<MessageCfg>();
}

其中,可以设置的项有:
ActivationCacheDisabled、AllowNullInjection、CachePruningInterval、ExtensionSearchPattern、InjectAttribute、InjectNonPublic等等。大概对应的就是设置缓存是否启用、是否允许空注入、缓存周期、扩展查找位置、必须被注入的属性、是否必须注入非公开成员等等。(BTW:Ninject中摘要说明少了Get,还发现好几个地方都是这样呢)。

 

5.        Inject特性
在Inject中,我们构造函数、属性和字段上加 [Inject]特性指示,以便指定注入的属性、方法和字段等。[Inject]特性定义具体如下:

[AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple =false, Inherited = true)]
   public class InjectAttribute : Attribute
   {
       public InjectAttribute();
   }

例如使用[Inject]指定构造函数,如果某个类存在多个构造函数,那么我们就可以在某个构造函数上使用[Inject]来指定从此处注入,具体代码如下:

public class MessageDB : IMessage
   {
       public MessageDB() { }
 
 
       public MessageDB(object msg)
       {
           Console.WriteLine("使用了object 参数构造:{0}", msg);
       }
 
       [Inject]
       public MessageDB(int msg)
       {
           Console.WriteLine("使用了int 参数构造:{0}", msg);
       }
       public string GetMsgNumber()
       {
           return "从数据中读取消息号!";
       }
   }

相应的MessageModule进行修改,具体如下:

public class MessageModule : NinjectModule
   {
       public override void Load()
       {
           //绑定接口并指定接口的实现。
           Bind<IMessage>().To<MessageDB>().WithConstructorArgument("msg", 1);
       }
}

具体使用如下:

using (var kernal = new StandardKernel(new MessageModule()))
           {
               //属性注入
               var msgcfg = new MessageCfg() { Msg = kernal.Get<IMessage>() };
               Console.WriteLine(msgcfg.GetMsgNumber());
               Console.Read();
           }

其中MessageCfg类的定义见前一节介绍的内容。通过上面的代码,我们可以看到,MessageDB分别由int和object类型的构造函数,如果没有在构造函数上指定[Inject],经过测试发现它默认就是选择第一个构造函数,如果参数类型不匹配就直接抛出异常。
6.        对象作用域:

Transient .InTransientScope() 每次调用创建新实例。
Singleton .InSingletonScope() 单例,仅创建一个实例。
Thread .InThreadScope() 每一个线程创建一个实例。
Request .InRequestScope() 每当Web请求发起时创建一个实例,结束请求时释放实例
InScope InScope(Func) 对象尽量保持到回调委托返回

上表来自“靠近太阳”的博文,后增加了InScope 。如使用InThreadScope()具体例子如下:

public class MessageModule : NinjectModule
   {
       public override void Load()
       {
           //绑定接口并指定接口的实现。
           Bind<IMessage>().To<MessageDB>().InThreadScope().WithParameter(new ConstructorArgument("msg", 1));
 
       }
   }

使用代码如下:

using (var kernal = new StandardKernel(new MessageModule()))
           {
               //属性注入
               var th1 = new Thread(new ThreadStart(() =>
               {
                   var msgcfg = new MessageCfg() { Msg = kernal.Get<IMessage>() };
                   Console.WriteLine(msgcfg.GetMsgNumber());
               }));
               var th2 = new Thread(new ThreadStart(() =>
               {
                   var msgcfg = new MessageCfg() { Msg = kernal.Get<IMessage>() };
                   Console.WriteLine(msgcfg.GetMsgNumber());
               }));
               var th3 = new Thread(new ThreadStart(() =>
               {
                   var msgcfg = new MessageCfg() { Msg = kernal.Get<IMessage>() };
                   Console.WriteLine(msgcfg.GetMsgNumber());
               }));
 
               th1.Start();
               th2.Start();
               th3.Start();
               Console.Read();
           }
       }

在上面的代码中,我们指定了对象在InThreadScope,在使用的代码中分别创建了3个线程来进行模拟,最终每个线程都是创建了一个对象。

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
出处:http://www.cnblogs.com/xray2005/

依赖注入和IOC的更多相关文章

  1. Atitit js中的依赖注入di ioc的实现

    Atitit js中的依赖注入di ioc的实现 全类名(FQCN)为标识符1 混合请求模式1 使用类内  builder  即可..2 Service locator method走ok拦2 Jav ...

  2. spring 依赖注入(IOC DI)

    依赖注入(IOC DI) 依赖注入的两种方式: 1. set注入 Spring要求使用set注入方式的时候,Bean需要提供一个无参数的构造方法.并提供一个属性的setter方法.例如: packag ...

  3. 依赖注入(IOC)二

    依赖注入(IOC)二 上一章我们讲了构造注入与设值注入,这一篇我们主要讲接口注入与特性注入. 接口注入 接口注入是将抽象类型的入口以方法定义在一个接口中,如果客户类型需要获得这个方法,就需要以实现这个 ...

  4. 依赖注入(IOC)

    依赖注入(IOC) 背景介绍 在设计模式中,尤其是结构型模式很多时候解决的就是对象间的依赖关系,变依赖具体为依赖抽象.平时开发中如果发现客户程序依赖某个或某类对象,我们常常会对他们进行一次抽象,形成抽 ...

  5. [ASP.NET Core 3框架揭秘] 依赖注入:IoC模式

    原文:[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式 正如我们在<依赖注入:控制反转>提到过的,很多人将IoC理解为一种“面向对象的设计模式”,实际上IoC不仅与面向对象没 ...

  6. C#中的依赖注入和IoC容器

    在本文中,我们将通过用C#重构一个非常简单的代码示例来解释依赖注入和IoC容器. 简介: 依赖注入和IoC乍一看可能相当复杂,但它们非常容易学习和理解. 在本文中,我们将通过在C#中重构一个非常简单的 ...

  7. 在 MVC 控制器中使用 构造函数时行依赖注入 (IoC)

    在 Controller 中使用 构造函数进行依赖注入 (IoC) 1. Controller 代码: ICard card; ICardCategory cardCategory; public C ...

  8. 【转】理解依赖注入(IOC)和学习Unity

    IOC:英文全称:Inversion of Control,中文名称:控制反转,它还有个名字叫依赖注入(Dependency Injection).作用:将各层的对象以松耦合的方式组织在一起,解耦,各 ...

  9. 理解PHP 依赖注入|Laravel IoC容器

    看Laravel的IoC容器文档只是介绍实例,但是没有说原理,之前用MVC框架都没有在意这个概念,无意中在phalcon的文档中看到这个详细的介绍,感觉豁然开朗,复制粘贴过来,主要是好久没有写东西了, ...

随机推荐

  1. 华为OJ: 公共字符串计算

    有几个需要注意的地方,这个问题是不是大写和小写之间的区别.这样你就输入字符串大写或小写转换的计算前. 第二个,定要清晰.先将s1从[i]处開始与s2的[j]開始匹配,不相等则j++直到j等于s2.le ...

  2. Object.extend

    原文:[转载]Object.extend Object.extend = function (destination, source) { for ( var property in source) ...

  3. 2014年最新的辛星html、css教程打包公布了,免积分,纯PDF(还有PHP奥)

    首先说一下,这个教程是我的全部的博客的精华,我整理了两天之后才做出的这个pdf文档,累死我了,以下免积分给大家,希望大家可以不吝指正,提出它的一些不足什么的,谢谢啦: 以下就是它的下载地址了:2014 ...

  4. 从头开始学JavaScript (二)——变量及其作用域

    原文:从头开始学JavaScript (二)--变量及其作用域 一.变量 ECMAscript变量是松散型变量,所谓松散型变量,就是变量名称可以保存任何类型的数据,每个变量仅仅是一个用于保存值的占位符 ...

  5. 基于Hama并联平台Finding a Maximal Independent Set 设计与实现算法

    笔者:白松 NPU学生. 转载请注明出处:http://blog.csdn.net/xin_jmail/article/details/32101483. 本文參加了2014年CSDN博文大赛,假设您 ...

  6. 西门子PLC学习笔记8-(计时器)

    计时器port这包括:信号输入.时间.复位信号.出口.[计时器剩余时间(BI二进制表示法.BCD码表示)其输出被存储MW] 定时器包括::S_PULSE(脉冲定时器).S_PEXT(延时脉冲定时器). ...

  7. [CLR via C#]4. 类型基础及类型、对象、栈和堆运行时的相互联系

    原文:[CLR via C#]4. 类型基础及类型.对象.栈和堆运行时的相互联系 CLR要求所有类型最终都要从System.Object派生.也就是所,下面的两个定义是完全相同的, //隐式派生自Sy ...

  8. C--运算符,表达式和语句实例

    //第五章 运算符,表达式和语句 #include<stdio.h> //引入头文件 #include<math.h> #define ADJUST 7.64 //定义常量 # ...

  9. 採用Hexo 搭建Team Blog

    採用Hexo 搭建Team Blog 首先你要会使用Git 已经GitHub,然后让我们了解下什么是静态Blog 静态博客是指 不须要数据库驱动,拥有丰富模板,通过模板标记语言生成简单html css ...

  10. C++程序中应增加STL、运算和字符串的头文件

    #include <complex> //模板类complex的标准头文件 #include <valarray> //模板类valarray的标准头文件 #include & ...