最近学习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