WCF技术剖析之二十: 服务在WCF体系中是如何被描述的?

2018-01-16 17:05:44 浏览数 (2)

任何一个程序都需要运行于一个确定的进程中,进程是一个容器,其中包含程序实例运行所需的资源。同理,一个WCF服务的监听与执行同样需要通过一个进程来承载。我们将为WCF服务创建或指定一个进程的方式称为服务寄宿(Service Hosting)。服务寄宿的本质通过某种方式,创建或者指定一个进程用以监听服务的请求和执行服务操作,为服务提供一个运行环境。

服务寄宿的方式大体分两种:一种是为一组WCF服务创建一个托管的应用程序,通过手工启动程序的方式对服务进行寄宿,所有的托管的应用程序均可作为WCF服务的宿主,比如Console应用、Windows Forms应用和ASP.NET应用等,我们把这种方式的服务寄宿方式称为自我寄宿(Self Hosting)。另一种则是通过操作系统现有的进程激活方式为WCF服务提过宿主,Windows下的进程激活手段包括IIS、Windows Service或者WAS(Windows Process Activation Service)等。

服务寄宿的手段是为一个WCF服务类型创建一个ServiceHost对象(或者任何继承于ServiceHostBase的对象)。无论采用哪种寄宿方式,在为某个服务创建ServiceHost的过程中,WCF框架内部会执行一系列的操作,其中最重要的步骤就是为服务创建服务描述(Service Description)。在本篇文章中,我们将对服务描述进行全面的介绍。

WCF服务描述通过类型System.ServiceModel.Description.ServiceDescription表示,ServiceDescription对象是WCF服务运行时的描述。除了包含WCF服务的一些基本信息,比如服务的名称、命名空间和CLR类型等,ServiceDescription还包含服务所有终结点和服务行为的描述。

一、 ServiceDescription与ServiceBehavior

从下面ServiceDescription的定义可以看出,ServiceDescription中定义了一系列属性,它们的含义如下:

  • Behaviors:服务行为(Service Behavior)的集合
  • ConfigurationName:服务的在配置文件中的名称,默认为服务类型的全名(命名空间 类型名称)
  • Name:服务的名称,默认为服务类型名称(不包含命名空间)
  • Namespace:服务的命名空间,默认为“http://tempuri.org/”
  • ServiceType:服务的CLR类型
代码语言:js复制
   1: public class ServiceDescription
   2: {
   3:     //其他成员
   4:     public KeyedByTypeCollection<IServiceBehavior> Behaviors { get; }
   5:     public string ConfigurationName { get; set; }
   6:     public ServiceEndpointCollection Endpoints { get; }
   7:     public string Name { get; set; }
   8:     public string Namespace { get; set; }
   9:     public Type ServiceType { get; set; }
  10: }

1、Name与Namespace

ServiceDescription的NameNamespace分别表示服务的名称和命名空间,这两个属性同样体现在服务发布的WSDL中。可以通过System.ServiceModel.ServiceBehaviorAttribute的NameNamespace属性进行设定。ServiceDescription的Name和Namespace的默认值分别为服务类型名称和http://tempuri.org/,所以下面两种定义是等效的。

代码语言:js复制
   1: [ServiceBehavior] 
   2: public class CalculatorService : ICalculator 
   3: { 
   4:     //省略成员 
   5: } 
代码语言:js复制
   1: [ServiceBehavior(Name = "CalculatorService", Namespace = "http://tempuri.org/")]
   2: public class CalculatorService : ICalculator
   3: {
   4:     //省略成员
   5: }

而ServiceDescription的Namespace映射WSDL的目标命名空间(targetNamespace),Name则直接对应<wsdl:service>节点的Name属性。在下面的服务定义中,通过ServiceBehaviorAttribute将Name和Namespace设置为“CalcService”和“http://www.artech.com/”,后面的XML体现了服务在WSDL表示。

代码语言:js复制
   1: [ServiceBehavior(Name = "CalcService", Namespace = "http://www.artech.com/")]
   2: public class CalculatorService : ICalculator
   3: {
   4:     //省略成员
   5: }
代码语言:js复制

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <wsdl:definitions name="CalcService" targetNamespace= http://www.artech.com/ 
   3: ...>
   4:     ......
   5:     <wsdl:service name="CalcService">
   6:         ......
   7:     </wsdl:service>
   8: </wsdl:definitions>

2、ConfigurationName

ServiceDescription的ConfiguraitonName表示服务的配置名称,可以同样可以通过System.ServiceModel.ServiceBehaviorAttribute的同名属性进行设定。在默认情况下,ConfiguraitonName的值为服务类型的全名(命名空间 类型名称),下面两种服务的定义是等效的。

代码语言:js复制
   1: namespace Artech.ServiceDescriptionDemos
   2: {    
   3:     [ServiceBehavior]
   4:     public class CalculatorService : ICalculator
   5:     {
   6:         //省略成员
   7:     }
   8: }
代码语言:js复制
   1: namespace Artech.ServiceDescriptionDemos
   2: {    
   3:     [ServiceBehavior(ConfigurationName = "Artech.ServiceDescriptionDemos.CalculatorService")]
   4:     public class CalculatorService : ICalculator
   5:     {
   6:         //省略成员
   7:     }
   8: }

如果配置文件中<service>的Name属性更改了,在服务定义中需要通过ServiceBehaviorAttribute对ConfigurationName进行相应的修正,如下面的代码所示。

代码语言:js复制
   1: namespace Artech.ServiceDescriptionDemos
   2: {    
   3:     [ServiceBehavior(ConfigurationName = "CalculatorService")]
   4:     public class CalculatorService : ICalculator
   5:     {
   6:      //省略成员
   7:     }
   8: }
代码语言:js复制
   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:     <system.serviceModel>        
   4:         <services>
   5:             <service name="CalculatorService">
   6:                 ......
   7:             </service>
   8:         </services>
   9:     </system.serviceModel>
  10: </configuration>

3、服务行为(Service Behavior)

如果说契约(Contract)是涉及双边的描述(契约是服务的提供者和服务消费者进行交互的依据),那么行为(Behavior)就是基于单边的描述。客户端行为体现的是WCF如何进行服务调用的方式,而服务端行为则体现了WCF的请求分发方式。行为是对WCF进行扩展的最为重要的方式,按照行为作用域的不同,WCF的行为大体包含以下四种:

  • 服务行为(Service Behavior):基于服务本身的行为,实现了接口System.ServiceModel.Description.IServiceBehavior,可以通过Attribute或者配置的方式进行指定
  • 终结点行为(Endpoint Behavior):基于某个服务终结点(客户端或者服务端)的行为,实现了接口System.ServiceModel.Description.IEndpointBehavior,可以通过配置的方式进行指定
  • 契约行为(Contract Behavior):基于某个服务契约的行为,作用于实现了该契约的所有服务(服务端行为)和基于该契约进行服务调用的服务代理(客户端行为),实现了接口System.ServiceModel.Description.IContractBehavior,可以通过Attribute的方式进行指定
  • 操作行为(Operation Behavior):基于服务契约中的某个操作契约,作用于实现了该服务契约的服务对应的服务操作(DispatchOperation)和基于该操作契约进行服务调用的客户操作(ClientOperation),实现了接口System.ServiceModel.Description.IOperationBehavior,可以通过Attribute进行指定

在ServiceDescription中,类型为KeyedByTypeCollection<IServiceBehavior>的Behaviors属性表示服务所有的服务行为集合。所有的服务行为都实现了System.ServiceModel.Description.IServiceBehavior接口。IServiceBehavior定义如下,从中可以看出IServiceBehavior定义了如下三个方法。

注:KeyedByTypeCollection<T>可以看成是以T实例为Value,Value对象真实类型为Key的Dictionary,可以通过类型定位并获取相应的成员对象。

  • AddBindingParameters:为某个自定义绑定元素(Custom Binding Element)添加绑定参数,以指导或者确保绑定元素的正常操作,比如通过设置的绑定参数创建相应的信道
  • ApplyDispatchBehavior:通过改变WCF服务端分发系统的属性,或者添加/替换分发系统中用以实现某种分发操作的可扩展对象,进而改变服务分发的行为
  • Validate:通过检验服务描述,用以保证后续工作的正常执行
代码语言:js复制
   1: public interface IServiceBehavior
   2: {
   3:     void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters);
   4:     void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase);
   5: void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase);
   6: }

WCF为我们预定义了一系列的ServiceBehavior,其中ServiceBehaviorAttribute就是其中之一。ServiceBehaviorAttribute不仅仅是一个自定义特性(Custom Attribute),实际上它本身就是一个实现了IServiceBehavior的服务行为。

代码语言:js复制
   1: [AttributeUsage(AttributeTargets.Class)]
   2: public sealed class ServiceBehaviorAttribute : Attribute, IServiceBehavior
   3: {
   4:     //省略成员
   5: }

对于其他一些预定义服务行为,比如用于实现与ASP.NET兼容的AspNetCompatibilityRequirementsAttribute;用于进行限流控制的ServiceThrottlingBehavior;用于进行服务授权的ServiceAuthorizationBehavior等,可以通过配置的方式应用于某个WCF服务。

对于ServiceDescription来说,最重要的要数System.ServiceModel.Description.ServiceEndpointCollection类型的Endpoints属性。该属性表示为服务添加的所有在终结点集合,集合的每个元素为System.ServiceModel.Description.ServiceEndpoint对象,接下来我们就来着重讨论ServiceEndpoint。

二、 ServiceEndpoint与EndpointBehavior

ServiceEndpoint对象是对终结点的运行时描述,终结点的三要素(ABC:Address、Binding、Contract)分别由同名的属性表示。ListenUri和ListenUriMode表示终结点真正的监听地址和监听模式,Address和ListenUri由被称为逻辑地址和物理地址(关于逻辑地址和物理地址,可以参考[原创]WCF后续之旅(15): 逻辑地址和物理地址)。

代码语言:js复制
   1: public class ServiceEndpoint
   2: {
   3:     //其他成员
   4:     public EndpointAddress Address { get; set; }
   5:     public KeyedByTypeCollection<IEndpointBehavior> Behaviors { get; }
   6:     public Binding Binding { get; set; }
   7:     public ContractDescription Contract { get; }
   8:     public Uri ListenUri { get; set; }
   9:     public ListenUriMode ListenUriMode { get; set; }
  10:     public string Name { get; set; }
  11: }

在ServiceEndpoint中,类型为KeyedByTypeCollection<IEndpointBehavior>的Behaviors属性表示绑定到该终结点的终结点行为(Endpoint Behavior)集合。集合的成员为实现了IEndpointBehavior接口的终结点行为对象。IEndpointBehavior的定义如下,AddBindingParameters、ApplyDispatchBehavior和Validate与IServiceBehavior同名方法语义类似。不同的是,IEndpointBehavior的所有方法的作用域仅限于当前终结点,并且IEndpointBehavior既可以作用于服务端,也可以用于客户端。为此,增加了一个新的方法:ApplyClientBehavior。ApplyClientBehavior方法与ApplyDispatchBehavior相对,通过修改客户端运行时(Client Runtime)的属性,或者添加/替换客户端运行时某些可扩展对象,进而实现控制客户端行为的目的。

代码语言:js复制
   1: public interface IEndpointBehavior
   2: {
   3:     void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters);
   4:     void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime);
   5:     void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher);
   6: void Validate(ServiceEndpoint endpoint);
   7: }

终结点的契约通过ContractDescription对象表示,为了让大家对服务描述有一个系统的认识,接下来我们继续介绍ContractDescription。

三、ContractDescription和ContractBehavior

System.ServiceModel.Description.ContractDescription定义了以下一些属性用于描述服务契约。由于服务契约通过System.ServiceModel.ServiceContractAttribute定义,所以大部分的属性都和ServiceContractAttribute的属性相匹配,在这里就不再作重复的介绍了。

代码语言:js复制
   1: public class ContractDescription
   2: {
   3:     //其他成员
   4:     public KeyedByTypeCollection<IContractBehavior> Behaviors { get; }
   5:     public OperationDescriptionCollection Operations { get; }
   6: }

在ContractDescription中,类型为KeyedByTypeCollection<IContractBehavior>的属性Behaviors代表基于服务契约的契约行为(Contract Behavior)集合,集合成员为实现了接口IContractBehavior的契约行为对象。IContractBehavior具有与IEndpointBehavior一样的方法成员,但是契约行为作用于实现了该服务契约的所有服务(服务端行为),基于使用该服务契约进行服务调用的服务代理(客户端行为)。

代码语言:js复制
   1: public interface IContractBehavior
   2: {
   3:     void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters);
   4:     void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime);
   5:     void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime);
   6: void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint);
   7: }

ContractDescription的Operations属性表示服务契约的所有操作的描述,类型为OperationDescriptionCollection,表示一个OperationDescription对象的集合。接下来,我们来介绍OperationDescription。

四、 OperationDescription和OperationBehavior

System.ServiceModel.Description.OperationDescription定义了一系列的属性用以描述定义在服务契约中操作契约。由于操作契约通过System.ServiceModel.OperationContractAttribute定义,所以OperationDescription的大部分属性与OperationContractAttribute属性一一匹配,在这里就不再作重复的介绍了。

代码语言:js复制
   1: public class OperationDescription
   2: {
   3:     //其他成员
   4:     public KeyedByTypeCollection<IOperationBehavior> Behaviors { get; }    
   5: }

上面我不止一次地提出客户端操作(Client Operation)和服务端操作(Dispatch Operation)的概念,这是由于在运行时,基于相同的OperationDescription创建操作对象在客户端和服务端是不同的,服务端操作称为分发操作(DispatchOperation),通过类型System.ServiceModel.Dispatcher.DispatchOperation 表示,客户端操作通过类型System.ServiceModel.Dispatcher.ClientOperation 表示。

在OperationDescription中,类型为KeyedByTypeCollection<IOperationBehavior>的Behaviors属性表示基于操作的所有操作行为(Operation Behavior)集合,集合成员为实现了IOperationBehavior接口的类型对象。IOperationBehavior具有与IEndpointBehavior、IContractBehavior一样的方法成员。IOperationBehavior的作用域仅限于当前的操作(客户端操作或者服务端操作)。

代码语言:js复制
   1: public interface IOperationBehavior
   2: {
   3:     void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters);
   4:     void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation);
   5:     void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation);
   6: void Validate(OperationDescription operationDescription);
   7: }

0 人点赞