在上一篇文章中,我们通过自定义InstanceProvider实现了WCF和微软Enterprise Library Unity Application Block的集成, 今天我们已相同的方式实现WCF与Enterprise Library的另一个Application Block的集成:Policy Injection Application Block (PIAB)。
PIAB,通过Method Interception的机制实现了AOP(Aspect Oriented Programing)。按照PIAB的编程方式,我们将非业务逻辑,比如Caching、Authorization、Transaction Enlist、Auditing、ExceptionHandling扽等等, 定义在一个个的CallHandler,这些CallHandler通过Attribute或者Configuration的方式应用到目标方法上。关于PIAB的详细介绍,我们参考我的PIAB系列(http://www.cnblogs.com/artech/archive/2008/01/29/1057379.html)。
由于PIAB特殊的实现机制(PIAB实现原理),我们需要通过PIAB的PolicyInjector来创建新的对象或者包装现有的目标对象。只有调用这种能够方式的对象,应用在上面的CallHandler才能被执行。所以WCF和PIAB的核心问题就是如何通过PIAB PolicyInjector来创建新的Service Instance,或者包装已经生成的service instance。在上面一篇文章中,我们通过Unity Container重新定义了InstanceProvider,我们今天的实现方案也是通过自定义InstanceProvider的方式来实现,不是我们需需要通过PolicyInjector来进行对象的创建。
下面是我们新的InstanceProvider(PolicyInjectionInstanceProvider )的定义
namespace Artech.WCFExtensions
2: {
class PolicyInjectionInstanceProvider : IInstanceProvider
4: {
private Type _serviceContractType;
string _policyInjectorName;
7:
string policyInjectorName)
9: {
this._serviceContractType = serviceContractType;
this._policyInjectorName = policyInjectorName;
12: }
object GetInstance(InstanceContext instanceContext, Message message)
14: {
null;
this._policyInjectorName))
17: {
new PolicyInjectorFactory().Create();
19: }
else
21: {
this._policyInjectorName);
23: }
24:
25: Type serviceType = instanceContext.Host.Description.ServiceType;
object serviceInstance = Activator.CreateInstance(serviceType);
is RemotingPolicyInjector)
28: {
return serviceInstance;
30: }
31:
this._serviceContractType);
33: }
34:
object GetInstance(InstanceContext instanceContext)
36: {
null);
38: }
39:
object instance)
41: {
as IDisposable;
null)
44: {
45: disposable.Dispose();
46: }
47: }
48: }
49: }
我们对PolicyInjectionInstanceProvider 的实现进行简单的说明:在PIAB中真正用于创建对象的是PolicyInjector,虽然PIAB中仅仅定义了一种基于Remoting的RemotingPolicyInjector,但是我们可以根据我们的需要实现一些不同Injection方式,比如IL Injection。所以我们定义了一个字段_policyInjectorName在配置中定位我们需要的PolicyInjector。该字段如果为null或者empty,将使用默认的PolicyInjector。PolicyInjection的获取通过下面的代码实现:
null;
this._policyInjectorName))
3: {
new PolicyInjectorFactory().Create();
5: }
else
7: {
this._policyInjectorName);
9: }
能够被RemotingPolicyInjector创建的对象不是满足下面两个条件中的一个:
- Target type实现一个Interface。
- Target Type直接或者间接集成System.MarshalByRefObject.
所以如果不能满足这个条件,我们直接通过反射创建service instance:
1: Type serviceType = instanceContext.Host.Description.ServiceType;
object serviceInstance = Activator.CreateInstance(serviceType);
is RemotingPolicyInjector)
4: {
return serviceInstance;
6: }
最后我们通过policyInjector 的Wrap方法对service instance进行封装并返回:
this._serviceContractType);
二、为PolicyInjectionInstanceProvider创建Behavior
我们可以通过ContractBehavior或者EndpointBehavior应用我们定义的PolicyInjectionInstanceProvider 。
I、ContractBehavior:PolicyInjectionBehaviorAttribute
namespace Artech.WCFExtensions
2: {
class PolicyInjectionBehaviorAttribute : Attribute, IContractBehavior
4: {
string PolicyInjectorName{ get; set; }
void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters){ }
void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime){ }
void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
9: {
10: Type serviceContractType = contractDescription.ContractType;
this.PolicyInjectorName);
12: }
void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint){
14: }
15: }
我们在ApplyDispatchBehavior,通过contractDescription.ContractType获得service contract type,然后创建我们的PolicyInjectionInstanceProvider, 并将其指定成当前DispatchRuntime 的InstanceProvider 。PolicyInjector通过属性PolicyInjectorName进行设置。
II、Endpoint Behavior & Behavior Extension: PolicyInjectionBehavior
namespace Artech.WCFExtensions
2: {
class PolicyInjectionBehavior : IEndpointBehavior
4: {
string _policyInjectorName;
string policyInjectorName)
7: {
this._policyInjectorName = policyInjectorName;
9: }
void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters){ }
void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime){ }
void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
13: {
14: Type serviceContractType = endpoint.Contract.ContractType;
this._policyInjectorName);
16: }
void Validate(ServiceEndpoint endpoint){ }
18: }
19: }
当前DispatchRuntime的InstanceProvider 在ApplyDispatchBehavior方法中指定,PolicyInjectorName通过配置文件配置。该配置节通过下面的PolicyInjectionBehaviorElement定义:
namespace Artech.WCFExtensions
2: {
class PolicyInjectionBehaviorElement:BehaviorExtensionElement
4: {
)]
string PolicyInjectorName
7: {
8: get
9: {
string;
11: }
12: set
13: {
value;
15: }
16: }
17:
override Type BehaviorType
19: {
typeof(PolicyInjectionBehavior); }
21: }
22:
object CreateBehavior()
24: {
this.PolicyInjectorName);
26: }
27: }
28: }
29:
三、应用我们的PolicyInjectionBehavior
现在模拟一个WCF的场景来应用我们创建的PolicyInjectionBehavior。为了直观我们我们创建一个Timeservice,用于返回当前的系统之间,然后我们运用PIAB的CachingCallHandler。那么我们可以通过返回值是否反映真正的当前时间来判断Policy Injection是否起作用了。我们依然采用我们的4层结构程序构架:
I、Artech.TimeService.Contract
namespace Artech.TimeService.Contract
2: {
3: [ServiceContract]
4: [PolicyInjectionBehavior]
interface ITime
6: {
7: [OperationContract]
8: DateTime GetCurrentTime();
9: }
10: }
我们先试验ContractBehavior,我们仅仅需要将PolicyInjectionBehaviorAttribute应用到ServiceContract上。
II、Artech.TimeService.Service
namespace Artech.TimeService.Service
2: {
class TimeService:ITime
4: {
5: [CachingCallHandler]
public DateTime GetCurrentTime()
7: {
return DateTime.Now;
9: }
10: }
11: }
我们在GetCurrentTime方法上应用了CachingCallHandlerAttribute,那么在第一次执行该方法的时候,方法返回的结果会被缓存,缓存的Key将会是方法和参数值的列表。后续的执行,将会直接从Cache中获取已经执行过的结果。
III、Artech.TimeService.Hosting
>
>
>
>
>
/>
>
>
/>
>
>
>
>
>
>
namespace Artech.TimeService.Hosting
2: {
class Program
4: {
string[] args)
6: {
typeof(Artech.TimeService.Service.TimeService)))
8: {
delegate
10: {
);
12: };
13: host.Open();
14:
15: Console.Read();
16: }
17: }
18: }
19: }
20:
IV、Artech.TimeService.Client
>
>
>
>
/>
>
>
>
namespace Artech.TimeService.Client
2: {
class Program
4: {
string[] args)
6: {
))
8: {
9: ITime proxy = channelFactory.CreateChannel();
10:
int i = 0; i < 10; i++)
12: {
13: Console.WriteLine(proxy.GetCurrentTime());
14: Thread.Sleep(1000);
15: }
16: }
17:
18: Console.Read();
19: }
20: }
21: }
22:
下面是最终输出的结果:
从返回的时间都是相同的,我们可以确认caching发挥了作用,如何我们将Contract上[PolicyInjectionBehavior]注释掉。
namespace Artech.TimeService.Contract
2: {
3: [ServiceContract]
//[PolicyInjectionBehavior]
interface ITime
6: {
7: [OperationContract]
8: DateTime GetCurrentTime();
9: }
10: }
我们将会得到这样的结果:
上面我们演示了ContractBehavior的应用,我们接着来演示EndpointBehavior的应用。我们仅仅需要修改Hosting的cnonfiguration就可以了:
>
>
>
>
>
>
/>
>
>
>
>
>
/>
>
>
>
>
/>
>
>
/>
>
>
>
>
>
>
此时运行我们的程序一样可以得到被返回值被Cache的结果:
出处:http://artech.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。