仍是那几句话:html
学无止境,精益求精mysql
十年河东,十年河西,莫欺少年穷sql
学历表明你的过去,能力表明你的如今,学习表明你的未来数据库
上篇博客介绍了依赖注入的三种方式:构造方法注入,属性注入,接口注入!详情请参考:学习 IOC 设计模式前必读:依赖注入的三种实现设计模式
本篇继续介绍IOC和DI的故事oracle
今天将以一个具体的IOC框架来介绍,Ninject 框架:框架
一、Ninject简介ide
Ninject是基于.Net平台的依赖注入框架,它可以将应用程序分离成一个个高内聚、低耦合(loosely-coupled, highly-cohesive)的模块,而后以一种灵活的方式组织起来。Ninject可使代码变得更容易编写、重用、测试和修改。函数
Ninject官方网址为:http://www.ninject.org/ 。sqlserver
二、项目引用Ninject.DLL 及 Ninject.Extensions.Xml.DLL
关于程序集的引用你们可自行下载DLL文件也能够经过NuGet管理器来下载,在此不做说明。
三、项目实例
和上篇博客同样,咱们经过具体例子来分享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
@陈卧龙的博客