WCF 扩展之我见: Behaviors

本系列索引,请见《开篇

上一篇中介绍了 Service Model Layer 中涉及到的方方面面,本篇文章的主角是起到桥梁作用的 Behaviors。


Description 和 Behaviors 简介

Description 提供了对 WCF 各部分组件的描述,包含静态的信息描述及对当前的服务、契约等的行为描述。主要的 Description 有如下三种:

ServiceDescription 表述了一个服务中的所有终结点、绑定、配置及 ServiceBehaviors。通过这个 Description,WCF 就可以为服务构建出所有运行时需要用到的组件。开发人员可以在没有调用 ServiceHost.Open 方法前通过使用 Description 对服务的运行时环境进行配置。

ContractDescription 表述了一个服务与外因进行通信的契约信息,包含了契约的名字、名称空间、会话模式、所有 Opertation 的描述及 ContractBehaviors。

OperationDescription 表述了一个操作对应的 Method 信息,所属 Contract 的 Description,所有的 KnownType,是否是 OneWay,该操作相关的 Message 的描述及 OperationBehaviors.


所有的 Behavior (行为)都位于 System.ServiceModel.Description 的名称空间下,通过 Behavior 可以对服务端/客户端的运行时对象进行扩展,而这些对象主要就是:服务端的 DispatchRuntime 和 DispatchOperation 或者客户端的 ClientRuntime 和 ClientOperation。


DispatchRuntime 和 DispatchOperation

既然扯到了这个,咱们不妨来细说一下。DispatchRuntime 和 DispatchOperation 都是服务端运行时对象,而它们都属于 EndpointDispatcher 的一部分。

WCF 中主要有两类 Dispatcher: ChannelDispatcher 和 EndpointDispatcher。前者与 ListenUri 一一对应,负责从 Channel 中获取消息并把消息分配给正确的 EndpointDispatcher。后者则用于调用对应的方法执行请求。

说回 DispatchRuntime,这个对象主要用于提供 WCF 运行过程中需要用到一些对象,比如 InstanceContextProvider、InstanceContextInitializers、InstanceProvider、MessageInspector、OperationSelector 等,这些对象往往作用于整个 Contract 范围。

DispatchRuntime 最终会把消息交给对应的 DispatchOperation,由 DispatchOperation 对消息进行最终的处理(调用实现的方法)。DispatchOperation 中包含了一些对象可以用来影响 Operation 范围内消息的处理流程,这其中包括:MessageFormatter、OperationInvoker 和 ParameterInspector。


四类 Behavior

对于不同的层次(Service\Endpoint\Contract\Operation),WCF 提供了四个不同的 Behavior 接口来进行对应层次的扩展:IServiceBehavior、IEndpointBehavior、IContractBehavior 和 IOperationBehavior。而它们实现扩展的方式其实就是修改上一节中所提到的那两对象中的不同属性。


这四个 Behavior 拥有相同的方法(参数类型和个数不一样):AddBindingParameters、ApplyDispatchBehavior、ApplyClientBehavior 和 Validate。其中 ServiceBehavior 只用于 Service 端,因此没有 ApplyClientBehavior。

AddBindingParameters: 主要用于向 Binding 中传递一些自定义的数据,以便于在后面的调用中使用。(由于使用不多,故不介绍)

ApplyDispatchBehavior: 主要用于向 DispatchRuntime\DispatchOperation 中关联上我们对 WCF 服务端的扩展。

ApplyClientBehavior: 用于向 ClientRuntime\ClientOperation 中关联上对 WCF 客户端的扩展。(本系列主要介绍服务端的扩展,故不介绍)

Validate: 检查所有的 Description 从而在服务正式启动之前确认服务可以被正确执行。(由于使用不多,故不介绍)


下面,来具体看一下每个 Behavior 的能力:

ServiceBehavior

适用于 Service 端,可以应用于所有 Endpoint 的 DispatchRuntime,只能应用于具体 Service 实现上。

public interface IServiceBehavior
{
    void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, bindingParameters);
 
    void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase);
 
    void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase);
}

留意一下 ApplyDispatchBehavior 这个方法,参数 ServiceHostBase 是服务宿主(ServiceHost) 的基类,其中暴露了 ChannelDispatcher 及 Description 这两个重要的属性,通过它们就可以访问到 EndpointDispatcher 及 所有的 Behaviors,而正如前面所说,对 WCF 的扩展基本就集中在 EndpointDispatcher 中。而参数 ServiceDescription 其实就是 ServiceHostBase 中的 Description 属性,两者是一致的,不晓得微软为什么要单独把这个属性拿出来作为一个参数。

下面演示了可以在 ServiceBehavior 中添加的扩展:

public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
    foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
    {
        channelDispatcher.ErrorHandlers.Add(new MyErrorHandler());
 
        foreach (EndpointDispatcher epDispatcher in channelDispatcher.Endpoints)
        {
            epDispatcher.AddressFilter = new MyAddressFilter();
            epDispatcher.ContractFilter = new MyContractFilter();
            epDispatcher.DispatchRuntime.InstanceContextInitializers.Add(new MyInstanceContextInitializer());
            epDispatcher.DispatchRuntime.InstanceContextProvider = new MyInstanceContextProvider();
            epDispatcher.DispatchRuntime.InstanceProvider = new MyInstanceProvider();
            epDispatcher.DispatchRuntime.MessageInspectors.Add(new MyDispatchMessageInspector());
            epDispatcher.DispatchRuntime.OperationSelector = new MyOperationSelector();
 
            foreach (DispatchOperation op in epDispatcher.DispatchRuntime.Operations)
            {
                op.CallContextInitializers.Add(new MyCallContextInitializer());
                op.Formatter = new MyDispatchMessageFormatter();
                op.Invoker = new MyOperationInvoker();
                op.ParameterInspectors.Add(new MyParameterInspector());                        
            }
        }
    }
}

上述代码中用了三个 foreach 来遍历所有 ChannelDispatcher,EndpointDispatcher 和 DispatchOperation,从而把自定义扩展应用到所有 Service。


EndpointBehavior

一个 Client 或 Service 可以有多个 Endpoint,只有应用了该行为的 Endpoint 才会获取该行为所附加的能力。

public interface IEndpointBehavior
{
    void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters);
 
    void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime);
 
    void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher);
 
    void Validate(ServiceEndpoint endpoint);
}

同样留意一下 ApplyDispatchBehavior,与 ServiceBehavior 不同,这里直接提供了 EndpointDispatcher,因此只能对该 Endpoint 进行扩展。

public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
    endpointDispatcher.AddressFilter = new MyAddressFilter();
    endpointDispatcher.ContractFilter = new MyContractFilter();
    endpointDispatcher.DispatchRuntime.InstanceContextInitializers.Add(new MyInstanceContextInitializer());
    endpointDispatcher.DispatchRuntime.InstanceContextProvider = new MyInstanceContextProvider();
    endpointDispatcher.DispatchRuntime.InstanceProvider = new MyInstanceProvider();
    endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MyDispatchMessageInspector());
    endpointDispatcher.DispatchRuntime.OperationSelector = new MyOperationSelector();
 
    foreach (DispatchOperation op in endpointDispatcher.DispatchRuntime.Operations)
    {
        op.CallContextInitializers.Add(new MyCallContextInitializer());
        op.Formatter = new MyDispatchMessageFormatter();
        op.Invoker = new MyOperationInvoker();
        op.ParameterInspectors.Add(new MyParameterInspector());
    }
}


ContractBehavior

一个 Contract 可以被多个 Endpoint 使用,如果希望所有使用了该 Contract 的 Endpoint 都拥有该行为,就可以使用 ContractBehavior。

public interface IContractBehavior
{
    void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters);
 
    void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime);
 
    void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime);
 
    void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint);
}

如何使用 ApplyDispatchBeahvior 请参考前面2个 Behavior。


OperationBehavior

适用于 Client 或 Service 端的服务方法,当某个方法被应用了 OperationBehavior 之后,Behavior 所附加的能力就会应用于该方法的调用过程中。

public interface IOperationBehavior
{
    void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters);
 
    void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation);
 
    void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation);
 
    void Validate(OperationDescription operationDescription);
}


如何应用自定义 Behavior

当实现了这四个 Behavior 后,可以通过如下几种方式把自定义的 Behavior 应用于我们的 WCF 应用程序中:

Attribute

定义 Behavior 的同时,继承 Attribute 类型,即通过声明 [XXXBehavior] 的方式来应用。比如在某个契约的定义上应用了[MyContractBehavior],也就意味着所有该契约的实现都将拥有 MyContractBehavior 所赋予的能力。

class MyBehavior :Attribute, IServiceBehavior
{...}


因为 Endpoint 没有相应的定义代码(即,public class XXXEndpoint ),所以无处使用 Attribute。

配置文件

写过 WCF 配置文件的童鞋肯定知道,配置文件可以添加自定义的 Service、Endpoint 及 Behavior 节点,因此可以使用该方式的 Behavior 为 ServiceBehavior 和 EndpointBehavior。


首先,继承 BehaviorExtensionElement

class MyBehaviorExtension : BehaviorExtensionElement
{
    public override Type BehaviorType
    {
        get { return typeof(MyBehavior); }
    }

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


其次,system.serviceModel\extensions\behaviorExtensions

    <extensions>
      <behaviorExtensions>
        <add name="myBehavior" type="ConsoleApplication1.MyBehaviorExtension,ConsoleApplication1"/>
      </behaviorExtensions>
    </extensions>


最后,添加到 system.serviceModel\behaviors

  <behaviors>
      <serviceBehaviors>        
        <behavior>
           <myBehavior />
        </behavior>
      </serviceBehaviors>
  </behaviors>


代码方式

服务宿主(ServiceHost)继承了 ServiceHostBase,该基类提供了 Description 属性,通过该属性可以访问到上面提到的四类 Behavior, 因此在 ServiceHost 启动之前,把自定义的 Behavior 添加到 Description 中即可实现与前两种一样的效果。

该方式最为灵活,适用于所有 Behavior,但是可读性不如 Attribute 强,可维护性又不如配置文件。


用表格来总结一下:

BehaviorAttribute配置文件代码方式
ServiceBehavior
ContractBehavior
EndpointBehavior
OperationBehavior


注,根据《消息处理流程》中所讲的,所有 Description 都是在 ServiceHost 启动的时候进行初始化,所以对应的 Behavior 同样在 ServiceHost 启动的时候会被初始化,这也意味着所有的扩展(如,MessageInspector)也会在那个时刻被初始化。而所有这些对象(Behavior 及 扩展对象)一旦创建便会一直存在于内存中,直到 Service 停止。基于这种情况,当我们在自定义扩展的时候,一定要考虑线程安全方面的因素。

参考资源

Configuring and Extending the Runtime with Behaviors

文章索引

[隐 藏]

本站采用知识共享署名 3.0 中国大陆许可协议进行许可。 ©2014 Charley Box | 关于本站 | 浙ICP备13014059号