WCF技术剖析之二十: 服务在WCF体系中是如何被描述的?
任何一个程序都需要运行于一个确定的进程中,进程是一个容器,其中包含程序实例运行所需的资源。同理,一个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类型
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的Name和Namespace分别表示服务的名称和命名空间,这两个属性同样体现在服务发布的WSDL中。可以通过System.ServiceModel.ServiceBehaviorAttribute的Name和Namespace属性进行设定。ServiceDescription的Name和Namespace的默认值分别为服务类型名称和http://tempuri.org/,所以下面两种定义是等效的。
1: [ServiceBehavior]
2: public class CalculatorService : ICalculator
3: {
4: //省略成员
5: }
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表示。
1: [ServiceBehavior(Name = "CalcService", Namespace = "http://www.artech.com/")]
2: public class CalculatorService : ICalculator
3: {
4: //省略成员
5: }
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的值为服务类型的全名(命名空间+类型名称),下面两种服务的定义是等效的。
1: namespace Artech.ServiceDescriptionDemos
2: {
3: [ServiceBehavior]
4: public class CalculatorService : ICalculator
5: {
6: //省略成员
7: }
8: }
1: namespace Artech.ServiceDescriptionDemos
2: {
3: [ServiceBehavior(ConfigurationName = "Artech.ServiceDescriptionDemos.CalculatorService")]
4: public class CalculatorService : ICalculator
5: {
6: //省略成员
7: }
8: }
如果配置文件中<service>的Name属性更改了,在服务定义中需要通过ServiceBehaviorAttribute对ConfigurationName进行相应的修正,如下面的代码所示。
1: namespace Artech.ServiceDescriptionDemos
2: {
3: [ServiceBehavior(ConfigurationName = "CalculatorService")]
4: public class CalculatorService : ICalculator
5: {
6: //省略成员
7: }
8: }
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:通过检验服务描述,用以保证后续工作的正常执行
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的服务行为。
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): 逻辑地址和物理地址)。
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)的属性,或者添加/替换客户端运行时某些可扩展对象,进而实现控制客户端行为的目的。
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的属性相匹配,在这里就不再作重复的介绍了。
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一样的方法成员,但是契约行为作用于实现了该服务契约的所有服务(服务端行为),基于使用该服务契约进行服务调用的服务代理(客户端行为)。
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属性一一匹配,在这里就不再作重复的介绍了。
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的作用域仅限于当前的操作(客户端操作或者服务端操作)。
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: }
出处:http://artech.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。