使用WCF扩展在方法调用前初始化环境
OperationInvoker 介绍
OperationInvoker 是 WCF 运行时模型中在调用最终用户代码前的最后一个扩展点,OperationInvoker 负责最终调用 Service Operation,并且在 IOperationInvoker 中定义了操作调用的同步和异步模式。
在 WCF 的内部,实现了同步和异步的方法调用类:
- System.ServiceModel.Dispatcher.SyncMethodInvoker
- System.ServiceModel.Dispatcher.AsyncMethodInvoker
上述两个实现是方法调用的默认实现。
IOperationInvoker 接口定义
1 // Summary:
2 // Declares methods that take an object and an array of parameters extracted
3 // from a message, invoke a method on that object with those parameters, and
4 // return the method's return value and output parameters.
5 public interface IOperationInvoker
6 {
7 bool IsSynchronous { get; }
8 object[] AllocateInputs();
9 object Invoke(object instance, object[] inputs, out object[] outputs);
10 IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state);
11 object InvokeEnd(object instance, out object[] outputs, IAsyncResult result);
12 }
问题描述
现在,我们需要在每个服务操作调用前为其单独准备 UnityContainer 环境,目的是保证每个服务操作调用所在的线程使用唯一个 UnityContainer。
假设,设计一个 UnityContainerScope 类来完成此工作。
1 public class UnityContainerScope : IDisposable
2 {
3 public static UnityContainerScope NewScope()
4 {
5 return new UnityContainerScope();
6 }
7
8 public void Dispose()
9 {
10
11 }
12 }
则服务实现中需要为每个操作添加 using (var scope = UnityContainerScope.NewScope()) {} 来完成 Scope 初始化。
1 public class CalculatorService : ICalculatorService
2 {
3 public int Add(int a, int b)
4 {
5 using (var scope = UnityContainerScope.NewScope())
6 {
7 return a + b;
8 }
9 }
10 }
解决方案
通过实现 IOperationInvoker 接口,在指定的 Operation 调用前直接调用 UnityContainerScope (仅实现同步接口调用) 。
1 public class UnityContainerScopeOperationInvoker : IOperationInvoker
2 {
3 private IOperationInvoker originalInvoker;
4
5 public UnityContainerScopeOperationInvoker(IOperationInvoker originalInvoker)
6 {
7 this.originalInvoker = originalInvoker;
8 }
9
10 #region IOperationInvoker Members
11
12 public object[] AllocateInputs()
13 {
14 return this.originalInvoker.AllocateInputs();
15 }
16
17 public object Invoke(object instance, object[] inputs, out object[] outputs)
18 {
19 using (var scope = UnityContainerScope.NewScope())
20 {
21 return this.originalInvoker.Invoke(instance, inputs, out outputs);
22 }
23 }
24
25 public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
26 {
27 return this.originalInvoker.InvokeBegin(instance, inputs, callback, state);
28 }
29
30 public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
31 {
32 return this.originalInvoker.InvokeEnd(instance, out outputs, result);
33 }
34
35 public bool IsSynchronous
36 {
37 get { return this.originalInvoker.IsSynchronous; }
38 }
39
40 #endregion
41 }
通过实现 UnityContainerScopeOperationBehaviorAttribute 来为需要初始化 Scope 的 Operation 进行定制。
1 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
2 public sealed class UnityContainerScopeOperationBehaviorAttribute : Attribute, IOperationBehavior
3 {
4 #region IOperationBehavior Members
5
6 public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
7 {
8 }
9
10 public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
11 {
12 }
13
14 public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
15 {
16 if (dispatchOperation != null)
17 {
18 dispatchOperation.Invoker = new UnityContainerScopeOperationInvoker(dispatchOperation.Invoker);
19 }
20 }
21
22 public void Validate(OperationDescription operationDescription)
23 {
24 }
25
26 #endregion
27 }
使用方式:
1 [ServiceContract]
2 public interface ICalculatorService
3 {
4 [OperationContract]
5 [UnityContainerScopeOperationBehavior]
6 int Add(int a, int b);
7 }
扩展实现
当然,通常定义 Contracts 的程序集比较纯粹干净,不会有多于的类库引用。而如果 UnityContainerScopeOperationBehaviorAttribute 定义在其他类库中,比如通用类库,则 Contracts 程序集则必须引用该类库。
我们可以通过使用 IEndpointBehavior 来进行行为扩展,而无需在每个 OperationContract 定义上 HardCode 。
1 public class UnityContainerScopeEndpointBehavior : IEndpointBehavior
2 {
3 #region IEndpointBehavior Members
4
5 public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
6 {
7 }
8
9 public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
10 {
11 }
12
13 public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
14 {
15 if (endpoint != null)
16 {
17 foreach (var operation in endpoint.Contract.Operations)
18 {
19 bool hasAdded = false;
20
21 foreach (var item in operation.Behaviors)
22 {
23 if (item.GetType().FullName == typeof(UnityContainerScopeOperationBehaviorAttribute).FullName)
24 {
25 hasAdded = true;
26 break;
27 }
28 }
29
30 if (!hasAdded)
31 {
32 operation.Behaviors.Add(new UnityContainerScopeOperationBehaviorAttribute());
33 }
34 }
35 }
36 }
37
38 public void Validate(ServiceEndpoint endpoint)
39 {
40 }
41
42 #endregion
43 }