仍是那幾句話: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
@陳臥龍的博客