博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
WCF后续之旅(8):通过WCF Extension 实现与MS Enterprise Library Policy Injection Application Block 的集成...
阅读量:6225 次
发布时间:2019-06-21

本文共 11298 字,大约阅读时间需要 37 分钟。

在中,我们通过自定义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系列()。

由于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: 
2: 
3:     
4:         
5:             
6:                 
7:                     contract="Artech.TimeService.Contract.ITime" />
8:                 
9:                     
10:                         
11:                     
12:                 
13:             
14:         
15:     
16: 
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: 
2: 
3:     
4:         
5:             
6:                 contract="Artech.TimeService.Contract.ITime" name="timeservice" />
7:         
8:     
9: 

1: namespace Artech.TimeService.Client
2: {
3:     class Program
4:     {
5:         static void Main(string[] args)
6:         {
7:             using (ChannelFactory
channelFactory = new ChannelFactory
("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: 
2: 
3:     
4:         
5:             
6:                 
7:                     
8:                 
9:             
10:         
11:         
12:             
13:                 
14:             
15:         
16:         
17:             
18:                 
19:                     contract="Artech.TimeService.Contract.ITime" />
20:                 
21:                     
22:                         
23:                     
24:                 
25:             
26:         
27:     
28: 
此时运行我们的程序一样可以得到被返回值被Cache的结果:

 

WCF后续之旅:

作者:蒋金楠
微信公众账号:大内老A
微博:
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号
蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
你可能感兴趣的文章
通用后台管理系统(3)-测试环境是否成功
查看>>
以Drools5.5为例说明“规则引擎在业务系统中应用”---实例
查看>>
[Python]网络爬虫(二):利用urllib2通过指定的URL抓取网页内容(转)
查看>>
Monkey and Banana(基础DP)
查看>>
leetcode-686-Repeated String Match(重复多少次A能够找到B)
查看>>
github page+jekyll构建博客的解决方案
查看>>
2013-7-22 确定鼠标与控件位置关系
查看>>
列、约束重命名,原数据不丢失
查看>>
【笔记】老程序员从头开始学JQuery的读书笔记02
查看>>
单点登录系统(一)
查看>>
[转]性能测试之性能计数器和监测工具
查看>>
HZAU1098: Yifan and War3(区间dp)
查看>>
html
查看>>
关于ajax中async: false的作用
查看>>
GitHub帮助文档翻译1——helloWorld
查看>>
文件的下载,随机验证码(无验证)登录注册
查看>>
第27章 java I/O输入输出流
查看>>
search-a-2d-matrix
查看>>
Ubuntu 12.04 Virtualbox 启用USB 设备支持
查看>>
C# DataTable的常用用法讲解
查看>>