实现WCF服务的一个常见的做法就是将程序分为服务、业务逻辑以及数据访问层。而这些层次之间的关系则恰好可以通过依赖注入容器在程序运行时进行关联。
Don Smith在服务架构概念模型(Service Architecture Concept Model)一文中提出了WCF服务门面的设计方法。依照这样的设计,服务将发布一个由业务逻辑层组件构成的接口/契约。而这些业务逻辑层组件则又依赖于资源访问层中的数据访问组件。
一般来讲,层与层之间的依赖关系均被硬编码在服务以及组件的实现代码中,这也让单元测试显得举步维艰。为了改善这种情况,Pablo M. Cibraro通过实现一个WCF依赖注入行为,在运行时将组件之间的关系进行注入。在他的方法中,作为依赖注入容器的是ObjectBuilder,而在Oran Dennison的WCF服务的依赖注入中,却异曲同工地选择了Spring.NET 。如下代码演示了Oran的实现方式中建立依赖关系的方法:
[ServiceContract]
public interface IServiceContract {
[OperationContract]
...
}
public class ServiceLayer : IServiceContract {
IBusinessLogic _businessLogic;
public ServiceLayer(IBusinessLogic businessLogic) {
_businessLogic = businessLogic;
}
...
}
public interface IBusinessLogic {
...
}
public class BusinessLogic : IBusinessLogic {
IDataAccess _dataAccess;
public BusinessLogic(IDataAccess dataAccess) {
_dataAccess = dataAccess;
}
...
}
public interface IDataAccess {
...
}
public class DataAccess : IDataAccess {
...
}
可以看到,服务层的实现则依赖于业务逻辑组件,而业务逻辑的实现则相应地依赖于数据访问组件。这样,建立依赖关系的代码将如下所示:
return new ServiceLayer(new BusinessLogic(new DataAccess()));
一般来讲,依赖注入容器是通过在配置中提供实现类到接口的映射关系来实现注入的。而这些配置则能够很容易地在运行时根据需要进行调整或改变。例如,有时为了方便单元测试,我们可能需要改用Mock对象。对于这种情况,Pablo给出了解决方案:
这简直是小菜一碟——(对于依赖注入容器)我们所要做的只是调整一下映射规则,否则容器无法知晓应该创建哪种对象的实例。现在就让我们通过一种可扩展的方法尝试配置该WCF服务。我们都知道,WCF支持一个名为IInstanceProvider的扩展,用来控制WCF实例的生命周期。我们正是要通过它来加载新的代码,并在运行时将层与层之间的依赖关系注入。public class DIInstanceProvider : IInstanceProvider {
[...]
public object GetInstance(InstanceContext instanceContext, Message message) {
DependencyContainer container = new DependencyContainer();
foreach (TypeMapping typeMapping in this.typeMappings) {
container.RegisterTypeMapping(typeMapping.TypeRequested, typeMapping.TypeToBuild);
}
return container.Get(this.serviceType);
}
[...]
}
随后,该DIInstanceProvider将通过一个IServiceBehavior在运行时接入到分配程序中。类型的映射是通过一个新的WCF配置节实现的,该配置节可以通过BehaviorExtensionElement从配置文件中读取,并传递给DIServiceBehavior实例。在Pablo给出的示例中,配置文件如下所示:
<behaviors>
<serviceBehaviors>
<behavior name="Behaviors1">
<dependencyInjection>
<typeMappings>
<add name="DataAccess" typeRequested="SampleService.ICustomerDataAccess, SampleService"
typeToBuild="SampleService.CustomerDataAccess, SampleService"/>
<add name="BusinessComponent" typeRequested="SampleService.ICustomerBusinessComponent, SampleService"
typeToBuild="SampleService.CustomerBusinessComponent, SampleService"/>
typeMappings>
dependencyInjection>
behavior>
serviceBehaviors>
behaviors>
Pablo给出了他的示例代码。我们也能够容易地将该代码转化为Oran等使用其他依赖注入容器(例如Castle Windsor)的实现方法。
查看英文原文:Injecting Implementation Dependencies into WCF Services