Ninject依赖注入(一)

Ninject学习笔记(一)

理解依赖注入

DI概念

依赖注入,或者控制反转,下降代码耦合度。Ninject是一个轻量级.NET DI框架。程序员

什么是DI?

一个例子,木匠伐木,须要工具斧子。正则表达式

 
 
 
 
class Carpenter{ Axe axe = new Axe(); void Lumber() { axe.Cut(); }}

代码中,木匠依赖于斧子。此时需求变了,木匠买了木锯,那么上面的代码必须重新修改而后进行编译。再好比木匠比较爱惜工具,决定两种工具换着用,再好比,木匠决定提升生产率,购置了电锯等等。做为程序员来讲,若是每次需求变动就从新编码,那么你会发现本身深陷沼泽地。编程

DI的出现就是为了解决这一问题的。它是一种编程方式,依赖关系不须要调用者来管理,统一由框架管理。“不要找咱们,咱们来找你”。缓存

DI是如何工做的?

简简单单一句话——对接口编程,而不是对具体实现编程。用抽象元素来实现依赖,而不是具体类,如此一来咱们能够很容易地替换具体的依赖类而不影响上层的调用组件。框架

 
 
 
 
class ITool{ void Cut();}class Carpenter{ private ITool tool; void Carpenter(ITool tool) { tool = tool; } void Lumber() { tool.Cut(); }}

什么是DI容器

DI容器是一个注入对象,用来向对象注入依赖性。一个应用中的依赖关系组成一个错综复杂的依赖图。DI容器就是来管理依赖复杂性的。它决定抽象类选择哪一个实现类来实例化对象。Ninject有两种定义依赖的方式:ide

  1. xml配置:<bind service="ITool to="Axe"/>
  2. 代码定义:`Bind ().To ()

使用Ninject

如何使用Ninject

  1. 在VS编辑环境中->右键项目->选择Nuget管理器->搜索Ninject->下载;
  2. 在项目中定义Kernel:var kernel = new StandardKernel()
  3. 经过kernel.Get方法获取依赖的对象。

Ninject对象生命周期

暂时范围

默认状态,Ninject无论理它建立的对象,也就是每次请求都new一个新对象。函数

单例范围

有两种方式建立单例工具

  1. 使用单例模式
 
 
 
 
class ConsoleLogger:ILogger{ public static readonly ConsoleLogger Instance = new ConsoleLogger(); private static ConsoleLogger() { // Hiding constructor } public void Log(string message) { Console.WriteLine("{0}: {1}", DateTime.Now, message); }}

而后在Bind方法后调用ToConstant方法指定静态只读对象ConsoleLogger.Instance为常量对象。
kernel.Bind<ILogger>().ToConstant(ConsoleLogger.Instance);学习

  1. 使用InSingletonScope方法——更简单的方法
    kernel.Bind<ILogger>().To<ConsoleLogger>().InSingletonScope();编码

  2. 指定某个类为单例
    kernel.Bind<ConsoleLogger>.ToSelf().InThreadScope();

线程范围

每个线程只建立一个给定类型的对象。对象的生命周期和线程同样长。
kernel.Bind<object>().ToSelf().InThreadScope();

请求范围

用在Web应用程序中很是有用。在相同的请求范围内获得一个单例的对象。须要添加Ninject.Web.Common引用。
kernel.Bind<SampleClass>().ToSelf().InRequestScope();

自定义范围

自定义范围让咱们定义咱们本身的范围,在这个范围内保持一类型的惟一对象。只要提供的回调方法返回的对象引用是同样的,Ninject在这个范围内返回相同的实例。只要返回的对象引用变了,将建立一新的指定类型的对象。建立的对象实例将一直保存在缓存里,直到返回的范围对象被垃圾回收器回收。一旦范围对象被垃圾回收器回收,Ninject建立的全部的对象实例将被从缓存中释放和处理。
调用InScope方法传入Lamda表达式定义自定义返回:kernel.Bind<object>().ToSelf().InScope( ctx => User.Current );

自定义范围是最灵活的,能够实现其余的范围:

  1. 线程范围:kernel.Bind<object>().ToSelf().InScope( ctx => Thread.CurrentThread);
  2. 请求范围:kernel.Bind<object>().ToSelf().InScope( ctx => HttpContext.Current);

Ninject模块

若是应用程序规模比较大,那么注册的服务列表将会很是长,维护变得困难。一种好的方式是进行分组管理。Ninject提供了这个功能。每一个组称为一个Ninject模块,只须要编写一个类实现INinjectModule接口,须要实现三个方法和两个属性。好消息是,Ninject还提供了一个实现该接口的抽象类NinjectModule,无需每次都实现接口的全部方法。
将多个模块加载到单个Ninject Kernel中的方法:
var kernel = new StandardKernel(new Module1(), new Module2(), ...)
也能够将应用程序中全部的模块同时加载到Ninject Kernel中:
kernel.Load(AppDomain.CurrentDomain.GetAssemblies());

从xml配置依赖(Ninject XML扩展)

须要Ninject XML扩展引用。注意记得发布xml文件时选择“Copy if newer”。
XML配置文件格式以下:

 
 
 
 
<module name="moduleName">  <bind service="Namespace.IService1, AssemblyName"    to="Namespace.ConcreteService1, AssemblyName" />  <bind service="Namespace.IService2, AssemblyName"    to="Namespace.ConcreteService2, AssemblyName"    Scope="singleton"/></module>

加载XML文件到Kernel的方法:
kernel.Load("module1.xml","module2.xml","module3.xml");
能够使用相对输出路径的路径,也能够使用绝对路径,还能够使用通配符:
kernel.Load("Modules/*.xml);

Ninject约定(Ninject Convention扩展)

小的应用中,一个一个注册服务类型并不困难,可是一个有上百个服务的应用程序呢?约定配置容许咱们绑定一组服务,而不是一个个分别绑定。

注册一个约定绑定须要三个步骤:选择包含具体类的程序集、选择程序集中的具体组件、选择具体组件相关的服务类型。

选择程序集

  1. FromThisAssembly():选择包含当前代码的程序集;
  2. From(params Assembly[] assemblies):选择指定程序集;
  3. FromAssemblyContaining<SomeType>():选择包含指定类的程序集;
  4. Join():选择多个程序集。
 
 
 
 
kernel.Bind(x => x.FromAssemblyContaining<CustomersService>().SelectAllClasses().Join().FromAssemblyContaining<MessageProvider>().SelectAllClasses().BindAllInterfaces());

默认状况下只有公有类型能够在程序集中被邦迪。为包含非公有类型,须要在选择程序集后显式调用IncludingNonePublicTypes方法:

 
 
 
 
kernel.Bind(x => x.FromAssemblyContaining<CustomersService>().IncludingNonePublicTypes().SelectAllClasses().BindAllInterfaces());

选择组件

选择要注册的组件。

  1. SelectAllClasses():选择全部的非抽象类;
  2. Select(Func<Type, bool> filter):选择须要的类。
    例子:选择以“Customer"开头的全部类:
 
 
 
 
kernel.Bind(r => r.FromThisAssembly().Select(t =>t.Name.StartsWith("Customer")).BindBase());

例子:用条件对结果进行过滤:

 
 
 
 
kernel.Bind(x => x.FromThisAssembly().SelectAllClasses().InNamespaces("Northwind.Controllers").BindBase());

选择服务类型

  • BindAllInterfaces(): 绑定全部的选择的组件的接口到选择的组件。
  • BindBase(): 绑定选择的组件的基类型到当前的组件。
  • BindDefaultInterface(): 绑定指定类型的默认接口到类型。类型的默认接口跟类型同名。例如,ICustomerService是CutomerService的默认接口。
  • BindDefaultInterfaces(): 绑定指定类型的默认接口到类型。类型的默认接口是那些以类型的名字结尾的接口。例如,IRepository和ICustomerRepository都是SqlCustomerRepository的默认接口。
  • BindSingleInterface(): 要求指定类型只有一个接口。在这个状况下,这个接口绑定到这个类型。若是这个类型没有或者有多个接口,则不添加绑定。
  • BindToSelf(): 绑定类型到自身。
  • BindSelection(ServiceSelector selector): 绑定选择的接口到类型。
  • BindUsingRegex(string pattern): 绑定当前类型的符合正则表达式的接口到类型。

绑定配置

绑定建立后,能够像普通绑定的配置同样进行配置:

 
 
 
 
kernel.Bind(x => x.FromThisAssembly().SelectAllClasses().BindAllInterfaces().Configure(b=>b.InSingletonScope()));

咱们也能够使用ConfigureFor 方法对某些类型分别进行配置。下面的例子,全部的repository类在构造函数中都注入"connectionString"参数,配置成单例生命周期。SqlCustomerRepository类重载成线程生命周期:

 
 
 
 
kernel.Bind(x => x.FromThisAssembly().SelectAllClasses().InheritedFrom<IRepository>().BindAllInterfaces().Configure(b =>b.InSingletonScope ().WithConstructorArgument("connectionString", ApplicationSettings.ConnectionString)).ConfigureFor<SqlCustomerRepository>(b =>b.InThreadScope()));


相关文章
相关标签/搜索