• 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层结构程序构架:

    wcf_02_08_01

    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:  

    下面是最终输出的结果:

    wcf_02_08_02

    从返回的时间都是相同的,我们可以确认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: } 

    我们将会得到这样的结果:

    wcf_02_08_03

    上面我们演示了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的结果:

    wcf_02_08_04

     

    WCF后续之旅:
    WCF后续之旅(1): WCF是如何通过Binding进行通信的
    WCF后续之旅(2): 如何对Channel Layer进行扩展——创建自定义Channel
    WCF后续之旅(3): WCF Service Mode Layer 的中枢—Dispatcher
    WCF后续之旅(4):WCF Extension Point 概览
    WCF后续之旅(5): 通过WCF Extension实现Localization
    WCF后续之旅(6): 通过WCF Extension实现Context信息的传递
    WCF后续之旅(7):通过WCF Extension实现和Enterprise Library Unity Container的集成
    WCF后续之旅(8):通过WCF Extension 实现与MS Enterprise Library Policy Injection Application Block 的集成
    WCF后续之旅(9):通过WCF的双向通信实现Session管理[Part I]
    WCF后续之旅(9): 通过WCF双向通信实现Session管理[Part II]
    WCF后续之旅(10): 通过WCF Extension实现以对象池的方式创建Service Instance
    WCF后续之旅(11): 关于并发、回调的线程关联性(Thread Affinity)
    WCF后续之旅(12): 线程关联性(Thread Affinity)对WCF并发访问的影响
    WCF后续之旅(13): 创建一个简单的WCF SOAP Message拦截、转发工具[上篇]
    WCF后续之旅(13):创建一个简单的SOAP Message拦截、转发工具[下篇]
    WCF后续之旅(14):TCP端口共享
    WCF后续之旅(15): 逻辑地址和物理地址
    WCF后续之旅(16): 消息是如何分发到Endpoint的--消息筛选(Message Filter)
    WCF后续之旅(17):通过tcpTracer进行消息的路由

  • 相关阅读:
    topcoder srm 640 div1
    具体数学第二版第四章习题(5)
    topcoder srm 635 div1
    topcoder srm 630 div1 (2-SAT and SCC template)
    具体数学第二版第四章习题(4)
    topcoder srm 625 div1
    具体数学第二版第四章习题(3)
    具体数学第二版第四章习题(2)
    topcoder srm 615 div1
    具体数学第二版第四章习题(1)
  • 原文地址:https://www.cnblogs.com/artech/p/1255258.html
Copyright © 2020-2023  润新知