最近學習MVC 看到很多文章都用了Ninject框架進行解耦,就考慮是否能用在平時寫的WCF服務中,因為畢竟目前還是總要寫服務的……蛋疼ing……
傳送門:
Ninject框架官網:
http://www.ninject.org/download.html
目前最新版本是3.0,另外需要下載WCF相關的擴展
Ninject 入門:
http://www.touchsunlight.com/coding/59.html
WCF 入門:
園子里找A大吧……
以下為正文,請大家耐心圍觀,不要高呼No Picture&Code You Say a JB…… 最后會提供DEMO下載
現在我們來看一個場景
我想服務有一個行為,可以返回一段字符串,我們可以直接定義這個服務行為如下
[ServiceContract] public class MessageService { [OperationContract] public string GetMessage() { return "Hello World"; } }
但是這樣不好,我不一定返回的就是Hello World這條消息,於是我們定義了一個接口,所有實現這個接口的類型都擁有返回消息的能力
public interface IMessageProvider { string GetMessage(); }
public class DefaultMessageProvider:IMessageProvider { public string GetMessage(){return "Hello World";} }
在服務里這么用
[ServiceContract] public class MessageService { private IMessageProvider provider=null; public MessageService(IMessageProvider provider) { this.provider=provider } [OperationContract] public string GetMessage() { return provider.GetMessage() } }
我們的想法是美好的,但現實是殘酷的,在對服務請求時,服務的實例,是由WCF管道創建的,我們無法手動new一個新的MessageService實例,也就代表我們無法通過構造函數向MessageService中注入IMessageProvider真正的實現類。
這時候,我們就要利用Ninject通過查找關系映射列表,自動創建合理的依賴項實例這個特性了。
這里簡單介紹下Ninject框架的幾個基本知識
Ninject 內核Kernel 是整個Ninject框架的主入口點,也是關系列表的容器。
我們可以通過IKernel kernel=new StandardKernel()來創建一個容器對象;
Kernel通過Bind<T>.To<T>語法建立接口與實例間的關系,例如:kernel.Bind<IMessageProvider>().To<DefaultMessageProvider>();
在建立接口與實現類之間的關系后,我們可以通過kenrel的Get<T>()方法來獲取正確的對象實例,例如:IMessageProvider provider= kernel.Get<IMessageProvider>();
這里我們獲取到的對象實例為DefaultMessageProvider的實例。
kernel對象在使用Get<T>()獲取類型的實例時,會檢查類型所有依賴項,並提供合理的值,我們看這個例子
public Interface IA { void Todo(); } public Interface IB { void Todo(); } Public Class A:IA { public void Todo(){ // Todo} } public Class B:IB { private IA a; public B(IA a) { this a = a; } public void Todo() { a.Todo(); } } public static Class Program { public static void Main() { IKernel kernel=new StandardKernel(); kernel.Bind<IA>().To<A>(); kernel.Bind<IB>().To<B>(); IB b=kernel.Get<IB>(); b.Todo(); } }
在這個例子中IB的實現類,依賴於IA的實現,並且使用構造函數的方式進行了依賴注入。
我們通過kernel.Get<IB>()時,會查找並創建B類型的實例,同時檢查發現B類依賴於IA接口,NInject會繼續檢查關系列表中是否存在IA的映射,並創建合理的實例。
最終返回B類型的實例,並且通過構造函數注入實現IA接口的類型的實例。
現在,我們來看Ninject的一個擴展Ninject.Extensions.Wcf
Ninject.Extensions.Wcf命名空間下提供了多種NinjectServiceHost以及他們的NinjectServiceHost<T>泛型版本。
我們在創建宿主的時候,不需要手動的去new一個ServiceHost實例。
可以直接通過kernel.Get<NinjectServiceHost<服務類型>>();的方式直接獲取宿主。
並且在服務宿主創建的時候,會自動檢查所有依賴關系。
示例代碼:
public static class Program { public static void Main() { IKernel kernel=new StandardKernel(); kernel.Bind<IMessageProvider>().To<DefaultMessageProvider>(); ServiceHost host=kernel.Get<NinjectServiceHost<MessageService>>(); } }
這樣我們在創建服務宿主的時候,就會關聯所有依賴了,也完美解決了之前遇到的問題。
這里再次提醒,NinjectServiceHost<T>中的T為服務類型,而非契約類型,即使在關系映射列表中添加了kenrel.Bind<契約>().To<服務>();也不行。
上邊這個例子應用於自托管服務。
還有一種服務的托管形式是IIS。
寄宿於IIS中的服務,就更簡單了。
只需要改變下Global父類為NinjectHttpApplication 位於Ninject.Web.Common命名空間下,提供一個全局的Kernel即可。
/// <summary> /// 這里要注意,實現NinjectHttpApplication /// </summary> public class Global : NinjectHttpApplication { protected override Ninject.IKernel CreateKernel() { //創建一個IOC容器,並且將服務管理模塊與內部接口映射模塊添加進去 return new StandardKernel(new ServiceModule(), new InternalModule()); } }
在服務的.svc頁,需要聲明Factory為NinjectServiceHostFactory,代碼如下
DemoService.svc
<%@ ServiceHost Language="C#" Service="NinjectSOAP.Service.Services.DemoService" Factory="Ninject.Extensions.Wcf.NinjectServiceHostFactory" %>
擴展閱讀:
Ninject.Extensions.Wcf命名空間提供的擴展方法InRequestScope() 表示了產生的對象實例,生命周期為每次Request時創建,響應/運行完畢回收
Ninject.Modules.NinjectModule 提供了依賴關系列表的模塊化管理
結語:
這篇文章不可否認很爛,其實也沒什么好寫的,很多東西擴展插件已經封裝好了。
但是在看老外的示例代碼時出了很多莫名其妙的問題,因此自己重新寫了一個DEMO,進行了整理和歸納。
同時也希望為有興趣在WCF中使用Ninject框架的朋友提供一份資料
心理明白……說不出來……一切盡在代碼中吧
附Demo下載:NinjectSOAP.rar