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
}

WCF提供了四种类型的行为:服务行为、终结点行为、契约行为和操作行为。 这四种行为分别定义了四个接口:IServiceBehavior,IEndpointBehavior,IContractBehavior以及 IOperationBehavior。虽然是四个不同的接口,但它们的接口方法却基本相同,分别为 AddBindingParameters(),ApplyClientBehavior()以及ApplyDispatchBehavior()。注 意,IServiceBehavior由于只能作用在服务端,因此并不包含ApplyClientBehavior()方法

我们可以定义自己的类实现这些接口,但需要注意几点:
1、行为的作用范围,可以用如下表格表示:

作用域

接口

影响范围

服务

终结点

契约

操作

服务

IServiceBehavior

Y

Y

Y

Y

终结点

IEndpointBehavior

Y

Y

Y

契约

IContractBehavior

Y

Y

操作

IOperationBehavior

Y

2、可以利用自定义特性的方式添加扩展的服务行为、契约行为和操作行为,但不能添加终结点行为;可以利用配置文件添加扩展服务行为和终结点行为,但不能添加契约行为和操作行为。但这些扩展的行为都可以通过ServiceDescription添加

利用特性添加行为,意味着我们在定义自己的扩展行为时,可以将其派生自Attribute类,然后以特性方式添加。例如:

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Interface)]
public class MyServiceBehavior:Attribute, IServiceBehavior...

[MyServiceBehavior]
public interface IService...

如果以配置文件的方式添加行为,则必须定义一个类继承自BehaviorExtensionElement(属 于命名空间System.ServiceModel.Configuration),然后重写属性BehaviorType以及 CreateBehavior()方法。BehaviorType属性返回的是扩展行为的类型,而CreateBehavior()方法则负责创建该扩展行为的对象实例:

public class MyBehaviorExtensionElement:BehaviorExtensionElement
{
    public MyBehaviorExtensionElement() { }
    public override Type BehaviorType
    {
        get { return typeof(MyServiceBehavior); }
    }

protected override object CreateBehavior()
    {
        return new MyServiceBehavior();
    }
}

如果配置的Element添加了新的属性,则需要为新增的属性应用ConfigurationPropertyAttribute,例如:

[ConfigurationProperty("providerName",IsRequired = true)]
public virtual string ProviderName
{
    get
    {
        return this["ProviderName"] as string;
    }
    set
    {
        this["ProviderName"] = value;
    }
}

配置文件中的配置方法如下所示:

<configuration>
  <system.serviceModel>  
    <services>
      <service name="MessageInspectorDemo.Calculator">
        <endpoint behaviorConfiguration="messageInspectorBehavior"
                  address="http://localhost:801/Calculator"                 
                  binding="basicHttpBinding"
                  contract="MessageInspectorDemo.ICalculator"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="messageInspectorBehavior">
          <myBehaviorExtensionElement providerName="Test"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <extensions>
      <behaviorExtensions>
        <add name="myBehaviorExtensionElement"
             type="MessageInspectorDemo.MyBehaviorExtensionElement, MessageInspectorDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
      </behaviorExtensions>
    </extensions>
  </system.serviceModel>
</configuration>

注 意,在<serviceBehaviors>一节中,<behavior>下 的<myBehaviorExtensionElement>就是我们扩展的行为,providerName则是 MyBehaviorExtensionElement增加的属性。如果扩展了IEndpointBehavior,则配置节的名称 为<endpointBehaviors>。<extensions>节负责添加自定义行为的扩展。其 中,<add>中的name值与<behavior>下 的<myBehaviorExtensionElement>对应。

特别注意的是<extensions>下的 type值,必须是类型的full name。第一个逗点前的内容为完整的类型名(包括命名空间),第二部分为完整的命名空间。Version,Culture以及 PublicKeyToken也是缺一不可的。每个逗点后必须保留一个空格,否则无法正确添加扩展行为的配置。这与反射有关,但太容易让人忽略这一小细节。希望微软能在后来的版本中修订这个瑕疵。

3、在行为扩展的适当方法中,需要添加参数检查、消息检查或操作调用程序的扩展。这之间存在一定的对应关系。对于参数检查,我们需要在IOperationBehavior接口类型中的ApplyClientBehavior()以及ApplyDispatchBehavior()中添加。例如对于之前的CalculatorParameterInspector,我们可以定义一个类CalculatorParameterValidation:

public class CalculatorParameterValidation:Attribute, IOperationBehavior
{
        #region IOperationBehavior Members
        public void AddBindingParameters(OperationDescription operationDescription,
            BindingParameterCollection bindingParameters)
        {
        }

public void ApplyClientBehavior(OperationDescription operationDescription,
            ClientOperation clientOperation)
        {
            CalculatorParameterInspector inspector = new CalculatorParameterInspector();
            clientOperation.ParameterInspectors.Add(inspector);
        }

public void ApplyDispatchBehavior(OperationDescription operationDescription,
            DispatchOperation dispatchOperation)
        {
            CalculatorParameterInspector inspector = new CalculatorParameterInspector();
            dispatchOperation.ParameterInspectors.Add(inspector);
        }

public void Validate(OperationDescription operationDescription)
        {
        }
        #endregion
}

如果检查器与扩展行为在职责上没有分离的必要,一个更好的方法是定义一个类同时实现IParameterInspector和IOperationBehavior接口,例如:

public class CalculatorParameterValidation:Attribute, IParameterInspector, IOperationBehavior
{
        #region IParameterInspector Members
        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;
        }
        #endregion

#region IOperationBehavior Members
        public void AddBindingParameters(OperationDescription operationDescription,
            BindingParameterCollection bindingParameters)
        {
        }

public void ApplyClientBehavior(OperationDescription operationDescription,
            ClientOperation clientOperation)
        {
            CalculatorParameterInspector inspector = new CalculatorParameterInspector();
            clientOperation.ParameterInspectors.Add(this);
        }

public void ApplyDispatchBehavior(OperationDescription operationDescription,
            DispatchOperation dispatchOperation)
        {
            CalculatorParameterInspector inspector = new CalculatorParameterInspector();
            dispatchOperation.ParameterInspectors.Add(this);
        }

public void Validate(OperationDescription operationDescription)
        {
        }
        #endregion
}

操作调用程序虽然通过IOperationBehavior进行关联,但确是通过DispatchOperation的Invoker属性。假定我们已经定义了一个实现IOperationInvoker的类MyOperationInvoker,则关联的方法为:

public class MyOperationInvokerBehavior : Attribute, IOperationBehavior
{
    #region IOperationBehavior Members
    public void AddBindingParameters(OperationDescription operationDescription,
        BindingParameterCollection bindingParameters)
    {
    }
    public void ApplyClientBehavior(OperationDescription operationDescription,
        ClientOperation clientOperation)
    {          
    }
    public void ApplyDispatchBehavior(OperationDescription operationDescription,
        DispatchOperation dispatchOperation)
    {
        dispatchOperation.Invoker = new MyOperationInvoker(dispatchOperation.Invoker);
    }
    public void Validate(OperationDescription operationDescription)
    {
    }
    #endregion
}

至 于对Dispatch的消息检查,则可以通过IServiceBehavior,IEndpointBehavior或 IContractBehavior中DispatchRuntime的MessageInspectors添加;而对Client的消息检查则可以通过 IEndpointBehavior或IContractBehavior中ClientRuntime的MessageInspectors添加(注 意,此时与IServiceBehavior无关,因为它不会作用于客户端代理)。例如:

public class PrintMessageInspectorBehavior : IDispatchMessageInspector,IEndpointBehavior
    {
        //略去IDispatchMessageInspector接口成员的实现;

#region IEndpointBehavior Members
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
            //empty;
        }
        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
              clientRuntime.MessageInspectors.Add(this);
        }
        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
        }
        public void Validate(ServiceEndpoint endpoint)
        {
            //empty;
        }
        #endregion
    }

如果实现的是IServiceBehavior接口,则需要遍历ApplyDispatchBehavior()方法中的ServiceHostBase对象:

public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
   foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
   {
      foreach (EndpointDispatcher endpointDispatcher in channelDispatcher .Endpoints)
      {
          endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
      }
   }
}

注:IDispatchMessageInspector 接口的方法为BeforeSendReply()和AfterReceiveRequest();而IClientMessageInspector接口 的方法则为BeforeSendRequest()和AfterReceiveReply()。

转载于:https://blog.51cto.com/wayfarer/280087

聚焦WCF行为的扩展相关推荐

  1. 通过WCF Extension定制扩展WCF行为

    功能介绍 当我们需要扩展WCF的功能,或者需要实现某些特定的功能,我们必须应用WCF的扩展定制功能(WCF extension),WCF framework提供了丰富的可扩展性,其提供的可扩展接口如下 ...

  2. 实现在GET请求下调用WCF服务时传递对象(复合类型)参数

    WCF实现RESETFUL架构很容易,说白了,就是使WCF能够响应HTTP请求并返回所需的资源,如果有人不知道如何实现WCF支持HTTP请求的,可参见我之前的文章<实现jquery.ajax及原 ...

  3. WCF 第十三章 可编程站点 所有都与URI相关

    普及的GET 方法 表13.1中所有URIs的一件共性的事情是它们都是用HTTP协议来访问资源.HTTP协议被认为是站点协议.HTTP协议的初衷是交换HTML页,但是它已经被用来访问所有类型的资源,包 ...

  4. [WCF]利用net.tcp傳輸協定來建置WCF Service

    http://www.dotblogs.com.tw/puma/archive/2009/06/21/wcf-net-tcp-channelfactory-clientbase.aspx 最近很少在寫 ...

  5. WCF简单教程(5) 用IIS做Host

    第五篇:用IIS做Host 之前几篇的Demo中,我们一直在用控制台程序做Server,今天换IIS来做Host,在Web Application中添加WCF的服务. 其实在Web Applicati ...

  6. WCF学习之旅—WCF概述(四)

    一.WCF概述 1) 什么是WCF? Windows Communication Foundation (WCF) 是用于构建面向服务的应用程序的框架.借助 WCF,可以将数据作为异步消息从一个服务终 ...

  7. [导入]WCF后传系列(8):深度通道编程模型Part 1—设计篇

    摘要: 从本质上说,WCF是一个通信服务框架,它允许我们使用不同的传输协议,使用不同的消息编码形式,跟不同的WS-*系列规范交互,而所有这些细节都是由通道堆栈来处理的.为了简化这些处理,在WCF中提供 ...

  8. 在WCF中使用Ninject轻量级IOC框架 之 SOAP风格服务

    最近学习MVC 看到很多文章都用了Ninject框架进行解耦,就考虑是否能用在平时写的WCF服务中,因为毕竟目前还是总要写服务的--蛋疼ing-- 传送门: Ninject框架官网: http://w ...

  9. 【金猿产品展】诸葛用户数据分析平台(Insight)——聚焦业务场景数据应用价值挖掘,赋能精细化运营...

    诸葛io产品 本产品由诸葛io投递并参与"数据猿年度金猿策划活动--2020大数据产业创新服务产品榜单及奖项"评选. 大数据产业创新服务媒体 --聚焦数据 · 改变商业 诸葛用户数 ...

最新文章

  1. Autodesk Forge Viewer与Forge API Node.js客户端SDK的TypeScript声明文件发布!
  2. 操作系统锁的实现方法有哪几种_「从入门到放弃-Java」并发编程-锁-synchronized...
  3. 2016蓝桥杯省赛---java---B---7(剪邮票)
  4. Diango博客--21.实现简单的全文搜索
  5. datetime报错 sql脚本_《SQL必知必会》附录A样例表的获取和导入
  6. 请使用recaptcha_如何在30分钟内使用ReCaptcha和PHP构建Bootstrap电子邮件表单
  7. Spark源码分析之BlockManager通信机制
  8. Linux 高性能集群搭建(1)---ssh节点通信
  9. Document类型、HTMLDocument类型和document对象的区别
  10. Shopee平台,对接运营经理是一条离成功更近的捷径
  11. CNN手写汉字识别参数调整
  12. 微信小程序-从0到1实现小程序内打开H5链接或跳转到某个公众号文章
  13. 接口自动化测试框架介绍
  14. SSLv3 协议漏洞‘POODLE’修复与相关概念
  15. 计算机u盘 硬盘无法读取,U盘在电脑上读取不出来怎么办?
  16. 卷积层的主要作用_对卷积神经网络CNN的理解,一文读懂卷积神经网络。
  17. MongoDB4.0 配置文件
  18. python基础操作笔记
  19. 速卖通AliExpress绑定连连跨境支付收款教程!
  20. 什么邮箱最安全?教你三招快速提升邮箱安全性方法,职场人必看!

热门文章

  1. Android开发系列之ListView
  2. 关于中值滤波算法,以及C语言实现(转)
  3. 驾照考试(科目三-大路)
  4. LDF文件丢失, 如何仅用MDF文件恢复数据库呢?
  5. CentOs 6.3_64静默安装oracle11g_r2
  6. 不聋不哑,不做当家之解
  7. 成员函数指针与高性能的C++委托(三)
  8. android之ListView和adapter配合显示图片和文字列表
  9. 如何掌握并在实践中自如运用设计模式
  10. golang中的goredis