您的位置:首页 > 移动开发

WCF后续之旅(8):通过WCF Extension 实现与MS Enterprise Library Policy Injection Application Block 的集成

2011-01-18 09:06 579 查看
上一篇文章中,我们通过自定义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的结果:

 

 

 





作者:Artech
出处:http://artech.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐