WCF后续之旅(8):通过WCF Extension 实现与MS Enterprise Library Policy Injection Application Block 的集成
在上一篇文章中,我们通过自定义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来进行对象的创建。
一、创建基于PolicyInjection的InstanceProvider
下面是我们新的InstanceProvider(PolicyInjectionInstanceProvider )的定义
1: namespace Artech.WCFExtensions
   2: {
3: public class PolicyInjectionInstanceProvider : IInstanceProvider
   4:     {
5: private Type _serviceContractType;
6: private string _policyInjectorName;
   7:  
8: public PolicyInjectionInstanceProvider(Type serviceContractType, string policyInjectorName)
   9:         {
10: this._serviceContractType = serviceContractType;
11: this._policyInjectorName = policyInjectorName;
  12:         }
13: public object GetInstance(InstanceContext instanceContext, Message message)
  14:         {
15: PolicyInjector policyInjector = null;
16: if (string.IsNullOrEmpty(this._policyInjectorName))
  17:             {
18: policyInjector = new PolicyInjectorFactory().Create();
  19:             }
20: else
  21:             {
22: policyInjector = new PolicyInjectorFactory().Create(this._policyInjectorName);
  23:             }
  24:  
  25:             Type serviceType = instanceContext.Host.Description.ServiceType;
26: object serviceInstance = Activator.CreateInstance(serviceType);
27: if (!this._serviceContractType.IsInterface && !serviceType.IsMarshalByRef && policyInjector is RemotingPolicyInjector)
  28:             {
29: return serviceInstance;
  30:             }
  31:  
32: return policyInjector.Wrap(serviceInstance, this._serviceContractType);
  33:         }
  34:  
35: public object GetInstance(InstanceContext instanceContext)
  36:         {
37: return this.GetInstance(instanceContext, null);
  38:         }
  39:  
40: public void ReleaseInstance(InstanceContext instanceContext, object instance)
  41:         {
42: IDisposable disposable = instance as IDisposable;
43: if (disposable != null)
  44:             {
  45:                 disposable.Dispose();
  46:             }
  47:         }
  48:     }
  49: } 
我们对PolicyInjectionInstanceProvider 的实现进行简单的说明:在PIAB中真正用于创建对象的是PolicyInjector,虽然PIAB中仅仅定义了一种基于Remoting的RemotingPolicyInjector,但是我们可以根据我们的需要实现一些不同Injection方式,比如IL Injection。所以我们定义了一个字段_policyInjectorName在配置中定位我们需要的PolicyInjector。该字段如果为null或者empty,将使用默认的PolicyInjector。PolicyInjection的获取通过下面的代码实现:
1: PolicyInjector policyInjector = null;
2: if (string.IsNullOrEmpty(this._policyInjectorName))
   3: {
4: policyInjector = new PolicyInjectorFactory().Create();
   5: }
6: else
   7: {
8: policyInjector = new PolicyInjectorFactory().Create(this._policyInjectorName);
   9: } 
能够被RemotingPolicyInjector创建的对象不是满足下面两个条件中的一个:
- Target type实现一个Interface。
- Target Type直接或者间接集成System.MarshalByRefObject.
所以如果不能满足这个条件,我们直接通过反射创建service instance:
   1: Type serviceType = instanceContext.Host.Description.ServiceType;
2: object serviceInstance = Activator.CreateInstance(serviceType);
3: if (!this._serviceContractType.IsInterface && !serviceType.IsMarshalByRef && policyInjector is RemotingPolicyInjector)
   4: {
5: return serviceInstance;
   6: } 
最后我们通过policyInjector 的Wrap方法对service instance进行封装并返回:
1: return policyInjector.Wrap(serviceInstance, this._serviceContractType);
二、为PolicyInjectionInstanceProvider创建Behavior
我们可以通过ContractBehavior或者EndpointBehavior应用我们定义的PolicyInjectionInstanceProvider 。
I、ContractBehavior:PolicyInjectionBehaviorAttribute
1: namespace Artech.WCFExtensions
   2: {
3: public class PolicyInjectionBehaviorAttribute : Attribute, IContractBehavior
   4:     {
5: public string PolicyInjectorName{ get; set; }
6: public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters){ }
7: public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime){ }
8: public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
   9:         {
  10:             Type serviceContractType = contractDescription.ContractType;
11: dispatchRuntime.InstanceProvider = new PolicyInjectionInstanceProvider(serviceContractType, this.PolicyInjectorName);
  12:         }
13: public 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
1: namespace Artech.WCFExtensions
   2: {
3: public class PolicyInjectionBehavior : IEndpointBehavior
   4:     {
5: private string _policyInjectorName;
6: public PolicyInjectionBehavior(string policyInjectorName)
   7:         {
8: this._policyInjectorName = policyInjectorName;
   9:         }
10: public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters){ }
11: public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime){ }
12: public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
  13:         {
  14:             Type serviceContractType = endpoint.Contract.ContractType;
15: endpointDispatcher.DispatchRuntime.InstanceProvider = new PolicyInjectionInstanceProvider(serviceContractType, this._policyInjectorName);
  16:         }
17: public void Validate(ServiceEndpoint endpoint){ }
  18:     }
  19: } 
当前DispatchRuntime的InstanceProvider 在ApplyDispatchBehavior方法中指定,PolicyInjectorName通过配置文件配置。该配置节通过下面的PolicyInjectionBehaviorElement定义:
1: namespace Artech.WCFExtensions
   2: {
3: public class PolicyInjectionBehaviorElement:BehaviorExtensionElement
   4:     {
5: [ConfigurationProperty("policyInjectorName",IsRequired = false, DefaultValue = "")]
6: public string PolicyInjectorName
   7:         {
   8:             get
   9:             {
10: return this["policyInjectorName"] as string;
  11:             }
  12:             set
  13:             {
14: this["policyInjectorName"] = value;
  15:             }
  16:         } 
  17:  
18: public override Type BehaviorType
  19:         {
20: get { return typeof(PolicyInjectionBehavior); }
  21:         } 
  22:  
23: protected override object CreateBehavior()
  24:         {
25: return new PolicyInjectionBehavior(this.PolicyInjectorName);
  26:         }
  27:     }
  28: } 
  29:  
三、应用我们的PolicyInjectionBehavior
现在模拟一个WCF的场景来应用我们创建的PolicyInjectionBehavior。为了直观我们我们创建一个Timeservice,用于返回当前的系统之间,然后我们运用PIAB的CachingCallHandler。那么我们可以通过返回值是否反映真正的当前时间来判断Policy Injection是否起作用了。我们依然采用我们的4层结构程序构架:
I、Artech.TimeService.Contract
1: namespace Artech.TimeService.Contract
   2: {
   3:     [ServiceContract]
   4:     [PolicyInjectionBehavior]
5: public interface ITime
   6:     {
   7:         [OperationContract]
   8:         DateTime GetCurrentTime();
   9:     }
  10: } 
我们先试验ContractBehavior,我们仅仅需要将PolicyInjectionBehaviorAttribute应用到ServiceContract上。
II、Artech.TimeService.Service
1: namespace Artech.TimeService.Service
   2: {
3: public class TimeService:ITime
   4:     {
   5:        [CachingCallHandler]
6: public DateTime GetCurrentTime()
   7:         {
8: return DateTime.Now;
   9:         } 
  10:     }
  11: } 
我们在GetCurrentTime方法上应用了CachingCallHandlerAttribute,那么在第一次执行该方法的时候,方法返回的结果会被缓存,缓存的Key将会是方法和参数值的列表。后续的执行,将会直接从Cache中获取已经执行过的结果。
III、Artech.TimeService.Hosting
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <system.serviceModel>
4: <services>
5: <service name="Artech.TimeService.Service.TimeService">
6: <endpoint behaviorConfiguration="" binding="basicHttpBinding"
7: contract="Artech.TimeService.Contract.ITime" />
8: <host>
9: <baseAddresses>
10: <add baseAddress="http://127.0.0.1/timeservice" />
11: </baseAddresses>
12: </host>
13: </service>
14: </services>
15: </system.serviceModel>
16: </configuration>
1: namespace Artech.TimeService.Hosting
   2: {
3: class Program
   4:     {
5: static void Main(string[] args)
   6:         {
7: using (ServiceHost host = new ServiceHost(typeof(Artech.TimeService.Service.TimeService)))
   8:             {
9: host.Opened += delegate
  10:                 {
11: Console.WriteLine("Time service has been started up!");
  12:                 };
  13:                 host.Open(); 
  14:  
  15:                 Console.Read();
  16:             }
  17:         }
  18:     }
  19: } 
  20:  
IV、Artech.TimeService.Client
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <system.serviceModel>
4: <client>
5: <endpoint address="http://127.0.0.1/timeservice" binding="basicHttpBinding"
6: contract="Artech.TimeService.Contract.ITime" name="timeservice" />
7: </client>
8: </system.serviceModel>
9: </configuration>
1: namespace Artech.TimeService.Client
   2: {
3: class Program
   4:     {
5: static void Main(string[] args)
   6:         {
7: using (ChannelFactory<ITime> channelFactory = new ChannelFactory<ITime>("timeservice"))
   8:             {
   9:                 ITime proxy = channelFactory.CreateChannel(); 
  10:  
11: for (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]注释掉。
1: namespace Artech.TimeService.Contract
   2: {
   3:     [ServiceContract]
4: //[PolicyInjectionBehavior]
5: public interface ITime
   6:     {
   7:         [OperationContract]
   8:         DateTime GetCurrentTime();
   9:     }
  10: } 
我们将会得到这样的结果:
上面我们演示了ContractBehavior的应用,我们接着来演示EndpointBehavior的应用。我们仅仅需要修改Hosting的cnonfiguration就可以了:
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <system.serviceModel>
4: <behaviors>
5: <endpointBehaviors>
6: <behavior name="PolicyInjectionBehavior">
7: <PolicyInjectionBehaviorExtension />
8: </behavior>
9: </endpointBehaviors>
10: </behaviors>
11: <extensions>
12: <behaviorExtensions>
13: <add name="PolicyInjectionBehaviorExtension" type="Artech.WCFExtensions.PolicyInjectionBehaviorElement, Artech.WCFExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
14: </behaviorExtensions>
15: </extensions>
16: <services>
17: <service name="Artech.TimeService.Service.TimeService">
18: <endpoint behaviorConfiguration="PolicyInjectionBehavior" binding="basicHttpBinding"
19: contract="Artech.TimeService.Contract.ITime" />
20: <host>
21: <baseAddresses>
22: <add baseAddress="http://127.0.0.1/timeservice" />
23: </baseAddresses>
24: </host>
25: </service>
26: </services>
27: </system.serviceModel>
28: </configuration>
此时运行我们的程序一样可以得到被返回值被Cache的结果:
出处:http://artech.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
 
                    
                     
                    
                 
                    
                




 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号