还是那几句话:

学无止境,精益求精

十年河东,十年河西,莫欺少年穷

学历代表你的过去,能力代表你的现在,学习代表你的将来

上篇博客介绍了依赖注入的三种方式:构造方法注入,属性注入,接口注入!详情请参考:学习 IOC 设计模式前必读:依赖注入的三种实现

本篇继续介绍IOC和DI的故事

今天将以一个具体的IOC框架来介绍,Ninject 框架:

1、Ninject简介

  Ninject是基于.Net平台的依赖注入框架,它能够将应用程序分离成一个个高内聚、低耦合(loosely-coupled, highly-cohesive)的模块,然后以一种灵活的方式组织起来。Ninject可以使代码变得更容易编写、重用、测试和修改。

  Ninject官方网址为:http://www.ninject.org/ 。

2、项目引用Ninject.DLL 及 Ninject.Extensions.Xml.DLL

  关于程序集的引用大家可自行下载DLL文件也可以通过NuGet管理器来下载,在此不作说明。

3、项目实例

  和上篇博客一样,我们通过具体例子来分享Ninject框架

  本篇继续采用上篇博客(学习 IOC 设计模式前必读:依赖注入的三种实现)案例进行说明,如下:

  首先,如同上篇博客背景一样,项目最初要求采用的是SqlServer数据库搭建,后来老板要求改为MySql数据库,再后来,老板要求改为Access数据库,再后来,老板又要求改为Oracle数据库,总之,这个老板的事很多...(请参考上篇博客)

  现在要求你设计一个解决方案,方便项目的扩展,你应该怎么设计?

  Ninject闪亮登场:

  首先,我们创建一个接口类,如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ConsoleNinject.Interface
{
public interface IDataAccess
{
void Add();
}
}

由于项目将来很可能变更数据库,因此,在项目构建之初我们应先将常用的数据库实现,如下:

Access数据库实现如下:

using ConsoleNinject.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ConsoleNinject.DAL
{
public class AccessDAL : IDataAccess
{
public void Add()
{
Console.WriteLine("在ACCESS数据库中添加一条订单");
}
}
}

MySql数据库实现如下

using ConsoleNinject.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ConsoleNinject.DAL
{
public class MySqlDAL : IDataAccess
{
public void Add()
{
Console.WriteLine("在MYSQL数据库中添加一条订单");
}
}
}

Oracle数据库实现如下:

using ConsoleNinject.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ConsoleNinject.DAL
{
public class OracleDAL : IDataAccess
{
public void Add()
{
Console.WriteLine("在Oracle数据库中添加一条订单");
}
}
}

SqlServer数据库实现如下:

using ConsoleNinject.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ConsoleNinject.DAL
{
public class SqlServerDAL : IDataAccess
{
public void Add()
{
Console.WriteLine("在SQLSERVER数据库中添加一条订单");
}
}
}

截止到现在,数据库层面的设计基本完成,现在我们来模仿一个下订单的类,分别采用构造方法注入和属性注入的方式,如下:

using ConsoleNinject.Interface;
using Ninject;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ConsoleNinject.UI
{
/// <summary>
/// 订单类-通过构造方法注入
/// </summary>
public class OrderCls
{
private IDataAccess _datadal; [Inject]
public OrderCls(IDataAccess DataDAL)
{
_datadal = DataDAL;
} public void Add()
{
_datadal.Add();
}
} /// <summary>
/// 订单类-通过属性注入
/// </summary>
public class OrderCls_SX
{
private IDataAccess _datadal; /// <summary>
/// 属性注入
/// </summary>
public IDataAccess DataDAL
{
get
{
return _datadal;
}
set
{
_datadal = value;
}
} public void Add()
{
_datadal.Add();
}
}
}

最后,便是利用NinJect框架来构建依赖关系并输出结果,如下:

using ConsoleNinject.DAL;
using ConsoleNinject.Interface;
using Ninject.Modules;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ConsoleNinject.UI
{
public class DataModule : NinjectModule
{
public override void Load()
{
Bind<IDataAccess>().To<AccessDAL>();
Bind<IDataAccess>().To<MySqlDAL>();
Bind<IDataAccess>().To<OracleDAL>();
Bind<IDataAccess>().To<SqlServerDAL>();
//
Bind<OrderCls>().ToSelf();
Bind<OrderCls_SX>().ToSelf();
} }
}

上述代码,注意继承的类及Bind()...To()方法,使用这个方法来确定类与接口之间的依赖关系

输出代码如下:

using ConsoleNinject.DAL;
using Ninject;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ConsoleNinject.UI
{
class Program
{
static void Main(string[] args)
{
IKernel kernal = new StandardKernel(new DataModule());
OrderCls mysql = new OrderCls(kernal.Get<MySqlDAL>()); // 构造函数注入
mysql.Add();
//
OrderCls access = new OrderCls(kernal.Get<AccessDAL>()); // 构造函数注入
access.Add();
//
OrderCls_SX oracle = new OrderCls_SX();
OracleDAL oracledal = new OracleDAL();//属性依赖注入
oracle.DataDAL = oracledal;
oracledal.Add();
//
OrderCls_SX sqlserver = new OrderCls_SX();
SqlServerDAL sqlserverdal = new SqlServerDAL();//属性依赖注入
sqlserver.DataDAL = sqlserverdal;
sqlserverdal.Add();
//
Console.ReadLine();
}
}
}

这样,整个项目就设计完了,四种数据库都实现了!老板应该可以闭嘴了,即使再要求换成另外一个类型的数据库,我们也不怕,只需增加相应的DAL层及依赖关系Module并修改输出即可!

这样,就基本符合设计模式的开闭原则,OrderCls代码内的业务逻辑代码是无需修改的!

但是,上述的方式仍然属于手动注入的方式,如何能做到动态配置呢?换句话说,如何能通过修改配置文件来完成动态配置呢?

Ninject是支持通过XML配置文件来实现动态注入的,这时需要引入:Ninject.Extensions.Xml.DLL

首先创建XML配置文件:

<?xml version="1.0" encoding="utf-8" ?>
<module name="ServiceModule">
<bind name="IDataAccess" service="ConsoleNinject.Interface.IDataAccess,ConsoleNinject.Interface" to="ConsoleNinject.DAL.SqlServerDAL,ConsoleNinject.DAL"/>
</module>

其次,书写Ninject XML 读取类,如下:

using Ninject;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Ninject.Extensions.Xml;
using System.Xml.Linq; namespace ConsoleNinject.UI
{
public class XMLModuleContext : IDisposable
{
public XMLModuleContext()
{
var settings = new NinjectSettings() { LoadExtensions = false };
Kernel = new StandardKernel(settings, new XmlExtensionModule());
} protected IKernel Kernel { get; private set; } public void Dispose()
{
this.Kernel.Dispose();
}
}
public class NinjectXMServiceLModule : XMLModuleContext
{
private static readonly NinjectXMServiceLModule instance = new NinjectXMServiceLModule();
protected readonly XmlModule module = null;
public NinjectXMServiceLModule()
{
var path = "D:/VS2012测试项目/ConsoleNinject/ConsoleNinject/Config/Ninject.xml"; //路径写死了 绝对路径
Kernel.Load(path);
module = Kernel.GetModules().OfType<XmlModule>().Single();
} public static IKernel GetKernel()
{
return instance.Kernel;
}
}
}

最后,输出端代码如下:

using ConsoleNinject.DAL;
using ConsoleNinject.Interface;
using Ninject;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ConsoleNinject.UI
{
class Program
{
static void Main(string[] args)
{
#region 手动注入
IKernel kernal = new StandardKernel(new DataModule());
OrderCls mysql = new OrderCls(kernal.Get<MySqlDAL>()); // 构造函数注入
mysql.Add();
//
OrderCls access = new OrderCls(kernal.Get<AccessDAL>()); // 构造函数注入
access.Add();
//
OrderCls_SX oracle = new OrderCls_SX();
OracleDAL oracledal = new OracleDAL();//属性依赖注入
oracle.DataDAL = oracledal;
oracledal.Add();
//
OrderCls_SX sqlserver = new OrderCls_SX();
SqlServerDAL sqlserverdal = new SqlServerDAL();//属性依赖注入
sqlserver.DataDAL = sqlserverdal;
sqlserverdal.Add();
//
#endregion #region 通过配置文件动态注入,说白了就是依赖关系写在了配置文件中
var kernel = NinjectXMServiceLModule.GetKernel();
var database = kernel.Get<IDataAccess>();
OrderCls ordcls = new OrderCls(database);
Console.WriteLine("我是通过配置文件确定的依赖关系!");
ordcls.Add();
#endregion
Console.ReadLine();
}
}
}

OK,上述便是整个Ninject的代码实现,下面转载下Ninject常用的方法:

(1)Bind<T1>().To<T2>()

其实就是接口IKernel的方法,把某个类绑定到某个接口,T1代表的就是接口或者抽象类,而T2代表的就是其实现类

例如:

IKernel ninjectKernel = new StandardKernel();
ninjectKernel.Bind<ILogger>().To<FileLogger>();

(2)Get<ISomeInterface>()

其实就是得到某个接口的实例,例如下面的栗子就是得到ILogger的实例FileLogger:

ILogger myLogger= ninjectKernel.Get<ILogger>();

(3)Bind<T1>() .To<T2>(). WithPropertyValue("SomeProprity", value);

其实就是在绑定接口的实例时,同时给实例NinjectTester的属性赋值,例如:

ninjectKernel.Bind<ITester>().To<NinjectTester>().WithPropertyValue("_Message", "这是一个属性值注入");

(4)ninjectKernel.Bind<T1>().To<T2>(). WithConstructorArgument("someParam", value);

其实就是说我们可以为实例的构造方法所用的参数赋值,例如:

public class DefalutDiscountHelper : IDiscountHelper
{
private decimal discountRate;
public decimal DiscountSize { get; set; }
public DefalutDiscountHelper(decimal discountParam)
{
discountRate = discountParam;
} public decimal ApplyDiscount(decimal totalParam)
{
return (totalParam - (discountRate / 100M * totalParam));
}
}
ninjectKernel.Bind<IDiscountHelper>().To<DefalutDiscountHelper>().WithConstructorArgument("discountParam", 50M);

(5)Bind<T1>().ToConstant()

这个方法的意思是绑定到某个已经存在的常量,例如:

StudentRepository sr = new StudentRepository();
ninjectKernel.Bind<IStudentRepository>().ToConstant(sr);

(6)Bind<T1>().ToSelf()

这个方法意思是绑定到自身,但是这个绑定的对象只能是具体类,不能是抽象类。为什么要自身绑定呢?其实也就是为了能够利用Ninject解析对象本身而已。例如:

ninjectKernel.Bind<StudentRepository>().ToSelf();
StudentRepository sr = ninjectKernel.Get<StudentRepository>();

(7)Bind<T1>().To<T2>().WhenInjectedInto<instance>()

这个方法是条件绑定,就是说只有当注入的对象是某个对象的实例时才会将绑定的接口进行实例化

ninjectKernel.Bind<IValueCalculater>().To<IterativeValueCalculatgor>().WhenInjectedInto<LimitShoppingCart>();

(8)Bind<T1>().To<T2>().InTransientScope()或者Bind<T1>().To<T2>().InSingletonScope()

这个方法是为绑定的对象指明生命周期其实

ninjectKernel.Bind<IStudentRepository>().To<StudentRepository>().InTransientScope();
//每次调用创建新实例
ninjectKernel.Bind<IStudentRepository>().To<StudentRepository>().InSingletonScope();
//每次调用是同一个实例

(9)Load()方法

这里的Load()方法其实是抽象类Ninject.Modules.NinjectModule的一个抽象方法,通过重写Load()方法可以对相关接口和类进行集中绑定,例如:

public class MyModule : Ninject.Modules.NinjectModule
{
public override void Load()
{
Bind<ILogger>().To<FileLogger>();
Bind<ITester>().To<NinjectTester>();
}
}

这是通过Load()方法绑定之后的完整代码:

private static IKernel kernel = new StandardKernel(new MyModule());
static void Main(string[] args)
{
ITester tester = kernel.Get<ITester>(); // 因为是链式解析,因此只解析ITester即可,其它依赖的东东都会顺带解析
tester.Test();
Console.Read();
}

(10)Inject属性

在Inject中,我们可以通过在构造函数、属性和字段上加 Inject特性指定注入的属性、方法和字段等,例如下面的栗子,MessageDB有两个构造函数:int和object类型的。现在我们已经为int型的指定了Inject特性,因此在注入的时候选择的就是int型的构造函数;如果没有在构造函数上指定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 "从数据中读取消息号!";
}
}

关于MVC中如何使用Ninject?本篇不作说明,我相信只要懂了基础,其他的Ninject的使用应该会手到擒来!

项目下载地址:https://files.cnblogs.com/files/chenwolong/ConsoleNinject.zip

@陈卧龙的博客

IOC框架之Ninject 简介的更多相关文章

  1. 轻量级IOC框架:Ninject

    Ninject 学习杂记 - liucy 时间2014-03-08 00:26:00 博客园-所有随笔区原文  http://www.cnblogs.com/liucy1898/p/3587455.h ...

  2. 轻量级IOC框架:Ninject (上)

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

  3. 轻量级IOC框架:Ninject (下)

    一,创建依赖链(Chains of Dependency) 当我们向Ninject请求创建一个类型时,Ninject会去检查该类型和其他类型之间的耦合关系.如果有额外的依赖,Ninject也会解析它们 ...

  4. IOC框架Ninject实践总结

    原文地址:http://www.cnblogs.com/jeffwongishandsome/archive/2012/04/15/2450462.html IOC框架Ninject实践总结 一.控制 ...

  5. 国人编写的开源 .net Ioc 框架——My.Ioc 简介

    My.Ioc 是作者开发的一款开源 IoC/DI 框架,下载地址在此处.它具有下面一些特点: 高效 在实现手段上,My.Ioc 通过使用泛型.缓存.动态生成代码.延迟注册.尽量使用抽象类而非接口等方式 ...

  6. 轻量级IoC框架Ninject.NET搭建

    说在之前的话 IOC的概念相信大家比较熟悉了,习惯性称之为依赖注入或控制反转,园子里对基于MVC平台IOC设计模式已经相当多了,但大家都只知道应该怎么应用一个IOC模式,比如Ninject, Unit ...

  7. 使用Microsoft的IoC框架:Unity来对.NET应用进行解耦

    1.IoC/DI简介 IoC 即 Inversion of Control,DI 即 Dependency Injection,前一个中文含义为控制反转,后一个译为依赖注入,可以理解成一种编程模式,详 ...

  8. 各大主流.Net的IOC框架性能测试比较

    Autofac下载地址:http://code.google.com/p/autofac/ Castle Windsor下载地址:http://sourceforge.net/projects/cas ...

  9. 主流IOC框架测验(.NET)

    上一篇中,我简单介绍了下Autofac的使用,有人希望能有个性能上的测试,考虑到有那么多的IOC框架,而主流的有:Castle Windsor.微软企业库中的Unity.Spring.NET.Stru ...

随机推荐

  1. python中关于类隐藏属性的三种处理方法

    关于隐藏属性 引子: 当类的属性或者类实例对象的属性隐藏的时候必须通过存取器方法来获取和设置这些隐藏的属性. 例如: def get_name(self,name):     #存取器方法 self. ...

  2. (网页)20个JS 小技巧超级实用

    转自CSDN: 1. 将彻底屏蔽鼠标右键 oncontextmenu=”window.event.returnValue=false”< table border oncontextmenu=r ...

  3. [20171031]rman xxx Failure.txt

    [20171031]rman xxx Failure.txt --//简单测试 List Failure, Advise Failure and Repair Failure命令在11g下,也许以后工 ...

  4. Unity LayerMask 的位运算

    Unity的Layer Unity是用 int32来表示32个Layer层,int32用二进制来表示一共有32位. 0000 0000 0000 0000 0000 0000 0000 0000 31 ...

  5. macOS 下NFS 文件系统挂载

    主要有两种方式: 使用:resvport选项, mount 挂载命令时. 使用:insecure选项, exportfs 文件配置时. sudo mount -o resvport IP:Addr b ...

  6. Win10更换电脑,又不想重装系统的解决方法

    问题描述: 在公司因为两年前用的i3的电脑很卡,然后想换i5的电脑,但是又不想重装系统,因为安装的东西太多了,重装很麻烦 Windows to go介绍: Windows To Go是Windows ...

  7. Azkaban-2.5.0-部署与常见案例

    该文章是基于 Hadoop2.7.6_01_部署 . Hive-1.2.1_01_安装部署 进行的 1. 前言 在一个完整的大数据处理系统中,除了hdfs+mapreduce+hive组成分析系统的核 ...

  8. 转载------------C函数之memcpy()函数用法

    转载于http://blog.csdn.net/tigerjibo/article/details/6841531 函数原型 void *memcpy(void*dest, const void *s ...

  9. Python用户名密码登录系统(MD5加密并存入文件,三次输入错误将被锁定)及对字符串进行凯撒密码加解密操作

    # -*- coding: gb2312 -*- #用户名密码登录系统(MD5加密并存入文件)及对字符串进行凯撒密码加解密操作 #作者:凯鲁嘎吉 - 博客园 http://www.cnblogs.co ...

  10. Spring集成JUnit单元测试框架

    一.JUnit介绍 JUnit是Java中最有名的单元测试框架,用于编写和运行可重复的测试,多数Java的开发环境都已经集成了JUnit作为单元测试的工具.好的单元测试能极大的提高开发效率和代码质量. ...