技术开发 频道

聚焦WCF行为的扩展

  【IT168 专稿】WCF以其灵活的可扩展架构为开发者提供了方便,其中对行为的扩展或许是应用中最为常见的。自定义对行为的扩展并不复杂,但仍有许多细节需要注意。

  在服务端,一般是对DispatchRuntime和DispatchOperation进行扩展,扩展点包括了对参数和消息的检查,以及操作调用程序, 它们对应的接口分别为IParameterInspector,IDispatchMessageInspector以及 IOperationInvoker。而在客户端,则是对ClientRuntime和ClientOperation进行扩展,扩展点包括对参数和消息 的检查,对应的接口分别为IParameterInspector和IClientMessageInspector。这些接口类型均被定义在 System.ServiceModel.Dispatcher命名空间下,其中IParameterInspector接口可以同时作用在服务端和客户端。

  对这些接口的实现,有点类似于AOP的实现,可以对方法调用前和调用后注入一些额外的逻辑,所以通常会将这些扩展称为侦听器。例如IParameterInspector接口,就定义了如下方法:

void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState);        
object BeforeCall(string operationName, object[] inputs);

  在调用服务对象的目标方法前,会调用BeforeCall方法,而在调用后则会调用AfterCall方法。例如我们可在方法调用前检验计算方法的参数是否小于0,如果小于0则抛出异常:

public class CalculatorParameterInspector:IParameterInspector
{
    
public void BeforeCall(string operationName, object[] inputs)
    {
        
int x = inputs[0] as int;
        
int y = inputs[1] as int;
        
if (x <0 || y < 0)
        {
          
throw new FaultException("The number can not be less than zero.");
        }
        
return null;
    }
    
public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
    {
        
//empty;
    }
}

  对消息的检查区分了服务端和客户端,接口方法根据消息传递的顺序刚好相反[注]。我们可以通过接口方法对消息进行处理,例如打印消息的Header:

public class PrintMessageInterceptor : IDispatchMessageInspector
    {
        
#region IDispatchMessageInspector Members

        
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            MessageBuffer buffer
= request.CreateBufferedCopy(Int32.MaxValue);
            request
= buffer.CreateMessage();

            Console.WriteLine(
"After Receive Request:");
            
foreach (MessageHeader header in request.Headers)
            {
                Console.WriteLine(header);
            }
            Console.WriteLine(
new string('*', 20));
            
return null;
        }

        
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
        {
            MessageBuffer buffer
= reply.CreateBufferedCopy(Int32.MaxValue);
            reply
= buffer.CreateMessage();

            Console.WriteLine(
"Before Send Request:");
            
foreach (MessageHeader header in reply.Headers)
            {
                Console.WriteLine(header);
            }
            Console.WriteLine(
new string('*', 20));
        }

        
#endregion
    }
0
相关文章