使用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 }