今天在开发博客园博客程序的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在单元测试中实现构造函数依赖注入)。