轻松实现WCF服务的构造函数依赖注入

今天在开发博客园博客程序的WCF服务时,想在“WCF服务实现”中通过构造函数进行依赖注入。代码如下:

    public class BlogService : IBlogService
{
private IBlogSiteService _blogSiteService;

public BlogService(IBlogSiteService blogSiteService)
{
_blogSiteService = blogSiteService;
}
}

依赖注入容器用的是Unity,IBlogSiteService的实现已经在WCF Host运行时通过Bootstrapper进行注入,参见寂寞如此美丽:脱离Application_Start,让初始化代码更优美

可是在客户端调用这个WCF服务时,却出现异常:

The service type provided could not be loaded as a service because it does not have a default (parameter-less) constructor.
To fix the problem, add a default constructor to the type, or pass an instance of the type to the host.

出现这个异常属正常现象,我没有告诉WCF Host,它怎么知道我要进行依赖注入,我们之间又没有心灵感应。WCF按常规办事,通过默认构造函数创建WCF服务的实例,所以引发异常。

那如何解决这个问题呢?

小软(微软)早就考虑到这一点了,提供了IInstanceProvider与IServiceBehavior接口。我们只需要实现这两个接口,并让实现IServiceBehavior的类成为一个Attribute(继承自Attribute),然后加在WCF服务实现类上,就可以实现WCF的构造函数依赖注入。

具体实现步骤如下:

一、实现IInstanceProvider接口 - IocInstanceProvider

1. 新建一个类IocInstanceProvider,实现IInstanceProvider接口。

2. 实现IInstanceProvider接口的三个方法,并引入你自己的IoC容器(比如我们用的是CNBlogs.Infrastructure.CrossCutting.IoC),也就是让WCF通过你的IoC容器获取WCF服务的实例。示例代码如下:

public class IocInstanceProvider : IInstanceProvider
{
Type _serviceType;
IContainer _container;

public IocInstanceProvider(Type serviceType)
{
_serviceType = serviceType;
_container = CNBlogs.Infrastructure.CrossCutting.IoC.
IoCFactory.Instance.CurrentContainter;
}

#region IInstanceProvider Members

public object GetInstance(InstanceContext instanceContext, Message message)
{
return _container.Resolve(_serviceType);
}

public object GetInstance(InstanceContext instanceContext)
{
return GetInstance(instanceContext, null);
}

public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
if (instance is IDisposable)
((IDisposable)instance).Dispose();
}

#endregion
}

注:你的IoC容器要事先注入了相应的WCF服务的实例。比如我们的注入:

container.RegisterType<IBlogService, BlogService>();

其中IBlogService是WCF服务接口,BlogService是WCF服务实现。

二、实现IServiceBehavior接口 - IocServiceBehavior

1. 新建一个类IocServiceBehavior,继承自Attribute,实现IServiceBehavior

public class IocServiceBehavior : Attribute, IServiceBehavior

2. 实现IServiceBehavior的AddBindingParameters()方法,并引入之前创建的IocInstanceProvider

public class IocServiceBehavior : Attribute, IServiceBehavior
{
#region IServiceBehavior Members

public void AddBindingParameters(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase,
Collection<ServiceEndpoint> endpoints,
BindingParameterCollection bindingParameters)
{
foreach (var item in serviceHostBase.ChannelDispatchers)
{
var dispatcher = item as ChannelDispatcher;
if (dispatcher != null)
{
dispatcher.Endpoints.ToList().ForEach(endpoint =>
{
endpoint.DispatchRuntime.InstanceProvider = new
IocInstanceProvider(serviceDescription.ServiceType);
});
}
}
}

public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{
}

public void Validate(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{
}

#endregion
}

三、在WCF服务实现类上增加[IocServiceBehavior]属性

代码如下:

[IocServiceBehavior]
public class BlogService : IBlogService
{
private IBlogSiteService _blogSiteService;

public BlogService(IBlogSiteService blogSiteService)
{
_blogSiteService = blogSiteService;
}

#region IBlogService Members

public BlogSiteDto GetBlogSiteWithPosts(int blogId,
bool withPostBody, int itemcount)
{
return _blogSiteService.GetWithPosts(blogId,
withPostBody, itemcount);
}

#endregion
}

搞定!是不是很轻松!

上面的实现代码参考自Domain Oriented N-Layered .NET 4.0 Sample App(http://microsoftnlayerapp.codeplex.com/),如果你对DDD感兴趣,推荐阅读这个项目的代码。

代码改进

根据Artech的建议,并参考WCF Extensibility – IInstanceProvider,改进一下IocServiceBehavior的代码,实现ApplyDispatchBehavior接口,代码如下:

public class IocServiceBehavior : Attribute, IServiceBehavior
{
#region IServiceBehavior Members

public void AddBindingParameters(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase,
Collection<ServiceEndpoint> endpoints,
BindingParameterCollection bindingParameters)
{
}

public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher cd in serviceHostBase.ChannelDispatchers)
{
foreach (EndpointDispatcher ed in cd.Endpoints)
{
if (!ed.IsSystemEndpoint)
{
ed.DispatchRuntime.InstanceProvider =
new IocInstanceProvider(serviceDescription.ServiceType);
}
}
}
}

public void Validate(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{
}

#endregion
}

小结

你IoC了吗?如果没有,你真的Out了。不仅ASP.NET可以轻松IoC(想爱容易,相处难:当ASP.NET MVC爱上IoC),而且单元测试也可以IoC(梦想成现实:用xUnit.net在单元测试中实现构造函数依赖注入)。

posted @ 2011-12-13 17:06  dudu  阅读(9818)  评论(16编辑  收藏  举报