前言

  之所以研究Ninject,是因为初入职在开发XX项目的ComponentService部分时用到了它,一下子发现了它的强大。渐渐地发现在项目中,有时会用到优秀的第三方开源库,这些都是前人智慧的结晶,值得学习和应用。

1.简介

Ninject(官网:http://www.ninject.org/)是一个快如闪电,超轻量级的基于的.Net平台的依赖注入框架。它能够帮助你把应用程序分离成一个个松耦合,高内聚的模块,然后用一种灵活的方式组装起来。通过使用Ninject配套你的软件架构,那么代码将会变得更加容易编写,重用性强,易于测试和修改。

Ninject is a lightning-fast, ultra-lightweight dependency injector for .NET applications. It helps you split your application into a collection of loosely-coupled, highly-cohesive pieces, and then glue them back together in a flexible manner. By using Ninject to support your software's architecture, your code will become easier to write, reuse, test, and modify.

2.特性

(1)“聚焦”:很多现有的依赖注入项目牺牲了特性的可用性,这通常是不必要的。Ninject每添加一个功能,其相较于增加复杂性的好处在于它提高了日常使用。我们的目标是保持知识的门槛(即使用Ninject所需的基本知识水平)尽可能地低。Ninject有很多高级特性,但是理解他们不需要使用基本功能。

Focused. Too many existing dependency injection projects sacrifice usability for features that aren't often necessary. Each time a feature is added to Ninject, its benefit is weighed against the complexity it adds to everyday use. Our goal is to keep the barrier to entry - the baseline level of knowledge required to use Ninject - as low as possible. Ninject has many advanced features, but understanding them is not required to use the basic features.

(2)”光滑“:框架膨胀是一些项目的主要关注点,这样,Ninject的所有核心功能是在单一程序集中对.NET基类库没有外部依赖。当编译为release版本时单个程序集的脚本大约85KB。

Sleek. Framework bloat is a major concern for some projects, and as such, all of Ninject's core functionality is in a single assembly with no dependencies outside the .NET base class library. This single assembly's footprint is approximately 85KB when compiled for release.

(3)“快速”。Ninject在CLR中利用轻量级代码生成的优点,而不是依靠反射调用。这在许多解决方案中能导致性能上8-50x的戏剧性提升。

Fast. Instead of relying on reflection for invocation, Ninject takes advantage of lightweight code generation in the CLR. This can result in a dramatic (8-50x) improvement in performance in many situations.

(4)“精确“:Ninject有助于开发人员在第一时间将事情做正确。Ninject并非依赖XML映射文件和字符串标识符来连接组件,而是提供了一个健壮的领域特定语言。这意味着,Ninject利用了语言(如类型安全)和IDE(如智能感知和代码自动完成)的功能优势。

Precise. Ninject helps developers get things right the first time around. Rather than relying on XML mapping files and string identifiers to wire up components, Ninject provides a robust domain-specific language. This means that Ninject takes advantage of the capabilities of the language (like type-safety) and the IDE (like IntelliSense and code completion).

(5)“敏捷“:Ninject是围绕基于组件的架构而设计,定制和演化。系统的许多方面可以增强或修改以适应每个项目的要求。

Agile. Ninject is designed around a component-based architecture, with customization and evolution in mind. Many facets of the system can be augmented or modified to fit the requirements of each project.

(6)“隐形”:Ninject不会侵犯你的代码。在你的项目中,可以很容易地分离Ninject对单一程序集的依赖。

Stealthy. Ninject will not invade your code. You can easily isolate the dependency on Ninject to a single assembly in your project.

(7)“强大”:Ninject包括许多高级功能。例如,Ninject是第一个支持上下文绑定的依赖注入容器,其中一个服务的不同具体实现根据请求的上下文而被注入。

Powerful. Ninject includes many advanced features. For example, Ninject is the first dependency injector to support contextual binding, in which a different concrete implementation of a service may be injected depending on the context in which it is requested.

3.应用总结及案例

综上所述:Ninjec可以理解成使程序脱离耦合的Ioc(控制反转)容器(推荐文章:深入理解DIP、IoC、DI以及IoC容器)。其设计初衷在于:

(1)某些框架过于依赖配置文件,配置较为复杂和冗长,需要提供assembly-qualified名称来进行定义,应用程序容易遭到破坏仅仅可能因为一个简单的拼写错误。而Ninject默认的做法是通过接口来进行类型绑定,如:将 ServiceImpl 的实现与接口 IService绑定

Bind<IService>().To<ServiceImpl>();

(插播一下Entity Framework的code first模式,用代码控制的思想还是不错的。参考文章:EF之code first使用

(图引自上文,扩展:ado .NET EF有三种设计模式:DBFirst,ModelFirst,CodeFirst)

(2)有些框架太重,通常需要在项目中添加非常多的组件引用,或依赖于各种框架组件。对于较小的项目,会导致代码“膨胀”。Ninject比较简洁明了。  

(3)Ninject 中有一个非常有用的概念:上下文绑定(Contextual Binding),与基于字符串标识的绑定不同,它可以运行期间灵活地进行条件化的绑定 :

Bind<IService>().To<RedImpl>().WhenTargetHas<RedAttribute>();
Bind<IService>().To<BlueImpl>().WhenTargetHas<BlueAttribute>();
class ConsumerA
{
public ConsumerA([Red] IService service)
{
//因为加上了"Red"定制特性,通过方法注入"IService"的实现将是RedImpl
}
}
class ConsumerB
{
public ConsumerB([Blue] IService service)
{
//因为加上了"Blue"定制特性,通过方法注入"IService"的实现将是BlueImpl
}
} 注:该部分代码示例引自:http://blog.csdn.net/simpkan/article/details/8169740

推荐文章:Ninject的使用(下面摘录)

DI容器的一个责任是管理他创建的对象的生命周期。他应该决定什么时候创建一个给定类型的对象,什么时候使用已经存在的对象。他还需要在对象不需要的时候处理对象。Ninject在不同的情况下管理对象的生命周期提供了强大的支持。在我们定义一个绑定的时候,定义创建对象的范围。在那个范围内,对象将被重用,每次绑定只存在一次。注意,对象不允许依赖于生命周期短自己小的对象。

1、暂时范围

在暂时态范围内,对象生命周期不被Ninject进行管理。任何时候请求一个类型的对象,都将创建一新对象。Ninject不管理保持创建的对象或者在范围内处理他。这是Ninject默认的范围。如果不指定范围,默认是暂时态。

namespace Ninject
{
class Program
{
static void Main(string[] args)
{
IKernel kernel = new StandardKernel(); kernel.Bind<ILogger>().To<ConsoleLogger>(); ILogger logger = kernel.Get<ILogger>(); logger.Log("Console log"); Console.ReadKey();
}
} interface ILogger
{
void Log(string message);
} class ConsoleLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine("{0}: {1}", DateTime.Now, message);
}
}
}

2、单例范围

有时候我们不想每次需要的时候都创建一个新的对象,这时候使用单例。有两种方法创建单例。一种是使用单例模式。一种是使用Ninject方法InSingletonScope。
1)使用单例模式:

namespace Ninject
{
class Program
{
static void Main(string[] args)
{
IKernel kernel = new StandardKernel(); kernel.Bind<ILogger>().ToConstant(ConsoleLogger.Instance); ILogger logger = kernel.Get<ILogger>(); logger.Log("Console log"); Console.ReadKey();
}
} interface ILogger
{
void Log(string message);
} class ConsoleLogger : ILogger
{
public static readonly ConsoleLogger Instance = new ConsoleLogger(); private ConsoleLogger()
{
} public void Log(string message)
{
Console.WriteLine("{0}: {1}", DateTime.Now, message);
}
}
}

2)使用方法InSingletonScope:

kernel.Bind<ILogger>().To<ConsoleLogger>().InSingletonScope();

如果要给MailServerConfig类对象设置单例,则先调用ToSelf方法将他绑定自身,然后再调用方法InSingletonScope:

kernel.Bind<MailServerConfig>().ToSelf().InSingletonScope();

3、线程范围

如果定义在线程范围内,每一个线程将只创建一个给定类型的对象。对象的生命周期跟对象所在的线程一样长。

调用方法InThreadScope创建线程范围:

kernel.Bind<object>().ToSelf().InThreadScope();

创建两个Test方法测试线程范围:

using Ninject;
using NUnit.Framework;
using System.Threading; namespace Demo.Ninject
{
[TestFixture]
class NinjectTest
{
[Test1]
public void ReturnsTheSameInstancesInOneThread()
{
using (var kernel = new StandardKernel())
{
kernel.Bind<object>().ToSelf().InThreadScope();
var instance1 = kernel.Get<object>();
var instance2 = kernel.Get<object>();
Assert.AreEqual(instance1, instance2);
}
} [Test2]
public void ReturnsDifferentInstancesInDifferentThreads()
{
var kernel = new StandardKernel();
kernel.Bind<object>().ToSelf().InThreadScope();
var instance1 = kernel.Get<object>();
new Thread(() =>
{
var instance2 = kernel.Get<object>();
Assert.AreNotEqual(instance1, instance2);
kernel.Dispose();
}).Start();
}
}
}

第一个方法在同一个线程内请求了两个object对象,他们是相同的实例。第二个方法先在主线程内请求一个object实例,然后开启另一个线程请求另一个实例,他们不是相同的实例。

需要添加NUnit和NUnit.Console才能测试上面的方法。我使用的是NUnit 2.6.4和NUnit.Console 2.0.0。

4、请求范围

请求范围在web应用程序里非常有用。可以在相同的请求范围内得到一个单例的对象。一旦一个请求被处理,另一个请求到来,Ninject创建新的对象实例,并保持他直到请求结束。

调用方法InRequestScope设置请求范围,例如:

kernel.Bind<MailServerConfig>().ToSelf().InRequestScope();

需要添加Ninject.Web.Common引用才能够调用InRequestScope方法。

5、自定义范围

自定义范围让我们定义我们自己的范围,在这个范围内保持一类型的唯一对象。只要提供的回调方法返回的对象引用是一样的,Ninject在这个范围内返回相同的实例。只要返回的对象引用变了,将创建一新的指定类型的对象。创建的对象实例将一直保存在缓存里,直到返回的范围对象被垃圾回收器回收。一旦范围对象被垃圾回收器回收,Ninject创建的所有的对象实例将被从缓存中释放和处理。

调用InScope方法传入Lamda表达式定义自定义返回。例如:

kernel.Bind<object>().ToSelf().InScope(ctx => User.Current);

用例子来介绍自定义范围:

1)创建User类:

class User
{
public string Name { get; set; }
public static User Current { get; set; }
}

2)创建函数ReturnsTheSameInstancesForAUser:

[Test]
public void ReturnsTheSameInstancesForAUser()
{
using (var kernel = new StandardKernel())
{
kernel.Bind<object>().ToSelf().InScope(ctx => User.Current);
User.Current = new User();
var instance1 = kernel.Get<object>();
User.Current.Name = "Foo";
var instance2 = kernel.Get<object>();
Assert.AreEqual(instance1, instance2);
}
}

虽然User.Current.Name的值变了,但是User.Current的引用没变。因此,两次请求返回的对象是同一个对象。

3)创建函数ReturnsDifferentInstancesForDifferentUsers:

[Test]
public void ReturnsDifferentInstancesForDifferentUsers()
{
using (var kernel = new StandardKernel())
{
kernel.Bind<object>().ToSelf().InScope(ctx => User.Current);
User.Current = new User();
var instance1 = kernel.Get<object>();
User.Current = new User();
var instance2 = kernel.Get<object>();
Assert.AreNotEqual(instance1, instance2);
}
}

因为改变了User.Current对象引用,因此,两次请求返回的对象是不同的对象。

你可能注意到了回调函数提供了一个名字是ctx的IContext参数。这个对象提供了绑定的用来创建范围对象的上下文环境。自定义范围是最灵活最有用的范围。其实其他范围都可以用自定义范围来实现。

线程范围:

kernel.Bind<object>().ToSelf().InScope(ctx=>Thread.CurrentThread);

请求范围:

kernel.Bind<object>().ToSelf().InScope(ctx=>HttpContext.Current);

可以使用Release方法强制释放创建的对象实例。

1 var myObject = kernel.Get<MyService>();
2 ..
3 kernel.Release(myObject);

C# Note3:大话Ninject的更多相关文章

  1. 大话设计模式(C#)

    还是那几句话: 学无止境,精益求精 十年河东,十年河西,莫欺少年穷 学历代表你的过去,能力代表你的现在,学习代表你的将来 问个问题: 如何写出高质量的代码?灵活,可扩展,易读,易维护,可重构,可复用. ...

  2. 大话keepalive

    大话keepalive 我们说到keepalive的时候,需要先明确一点,这个keepalive说的是tcp的还是http的. tcp的keepalive是侧重在保持客户端和服务端的连接,一方会不定期 ...

  3. Ninject学习(一) - Dependency Injection By Hand

    大体上是把官网上的翻译下而已. http://www.ninject.90iogjkdcrorg/wiki.html Dependency Injection By Hand So what's Ni ...

  4. ASP.NET MVC学前篇之Ninject的初步了解

    ASP.NET MVC学前篇之Ninject的初步了解 1.介绍 废话几句,Ninject是一种轻量级的.基础.NET的一个开源IoC框架,在对于MVC框架的学习中会用到IoC框架的,因为这种IoC开 ...

  5. [ASP.NET MVC 小牛之路]04 - 依赖注入(DI)和Ninject

    本人博客已转移至:http://www.exblr.com/liam  为什么需要依赖注入 在[ASP.NET MVC 小牛之路]系列的理解MVC模式文章中,我们提到MVC的一个重要特征是关注点分离( ...

  6. [ASP.NET MVC 小牛之路]05 - 使用 Ninject

    在[ASP.NET MVC 小牛之路]系列上一篇文章(依赖注入(DI)和Ninject)的末尾提到了在ASP.NET MVC中使用Ninject要做的两件事情,续这篇文章之后,本文将用一个实际的示例来 ...

  7. 我也来说说DDD~大话目录

    回到占占推荐博客索引 DDD之前没有接触过,但一但有了接触就一发不可收拾,他会带去进入一个全新的世界! DDD不是新技术,而是新思想,新模式,是软件开发领域的一次突破,它更接近于业务,对于业务的改动它 ...

  8. Atitti 大话存储读后感 attilax总结

    Atitti 大话存储读后感 attilax总结 1.1. 大话存储中心思想(主要讲了磁盘文件等存储)1 1.2. 最耐久的存储,莫过于石头了,要想几千万年的存储信息,使用石头是最好的方式了1 1.3 ...

  9. 大熊君大话NodeJS之------Connect中间件模块(第一季)

    一,开篇分析 截止到今天来说,NodeJS系列文章已经有将近十篇了,让我们回顾一下: (1),大熊君大话NodeJS之开篇------Why NodeJS(将Javascript进行到底) (2),大 ...

随机推荐

  1. 在Linux上搭建VisualSVN Server(svn服务端)

    一.检查是否安装了低版本的SVN #  rpm -qa | grep subversion 如果已安装SVN,则会返回版本信息.这时需要卸载旧版本的SVN. 卸载旧版本SVN # yum remove ...

  2. WiFi-ESP8266入门http(3-1)网页认证上网-post请求(原教程)

    教程:http://geek-workshop.com/thread-37484-1-1.html 源码:链接:https://pan.baidu.com/s/1yuYYqsM-WSOb0AbyAT0 ...

  3. ganache与metamask

    1.其实ganache其实就相当于一个私有链ganache安装,这个是图形化界面的: 2.(testRpc跟他其实是一个用处,有一个即可,只不过testRpc是非图形化界面.要注意两者都仅运行在內存中 ...

  4. 变量的值与判断的结果有关,使用Set Variable If

    1.有时候变量的值在赋值时,会依据情况来赋值.这个时候就要用到:Set Variable If ${result} BuiltIn.Set Variable 10 ${my_result}= Buil ...

  5. PAT A1052 Linked List Sorting (25 分)——链表,排序

    A linked list consists of a series of structures, which are not necessarily adjacent in memory. We a ...

  6. Tencent Cloud Developers Conference(2018.12.15)

    时间:2018.12.15地点:北京朝阳悠唐皇冠假日酒店

  7. EntityFramework Core并发深挖详解,一纸长文,你准备好看完了吗?

    前言 之前有关EF并发探讨过几次,但是呢,博主感觉还是有问题,为什么会觉得有问题,其实就是理解不够透彻罢了,于是在项目中都是用的存储过程或者SQL语句来实现,利用放假时间好好补补EF Core并发的问 ...

  8. Apache Commons Codec的Base64加解密库

    下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi import org.apache.commons.cod ...

  9. NLP之——Word2Vec详解

    2013年,Google开源了一款用于词向量计算的工具--word2vec,引起了工业界和学术界的关注.首先,word2vec可以在百万数量级的词典和上亿的数据集上进行高效地训练:其次,该工具得到的训 ...

  10. 01 前言/基础设施 - DevOps之路

    01 前言/基础设施 - DevOps之路 文章Github地址,欢迎start:https://github.com/li-keli/DevOps-WiKi 简介 基础架构采用DevOps设计思想, ...