今天介绍WCF 4.0的另外两个新特性:标准终结点(Standard Endpoint)和无(.SVC)文件服务激活(File-Less Activation)。前者实现了针对典型通信场景对终结点的定制,后者让你在进行IIS/WAS的服务寄宿中无须定义.svc文件。
一、标准终结点
我们知道,绑定的本质就是一系列相关绑定元素的有序集合,而系统绑定就是基于若干典型的通信场景对相关绑定元素的整合。WCF通过系统绑定对绑定元素进行了定制,那么能否在终结点级别对组成该终结点的ABC(地址、绑定和契约)也进行相应的定制呢?实际上这对于最新版本的WCF是可行的,我们将这个机制称为“标准终结点”。
所谓标准终结点,就是针对典型的通信场景选择组成终结点的要素(主要是绑定和契约)进而创建出一个标准的终结点。在使用的时候,如果你需要的终结点要素和标准终结点完全一致,就无需进行重复的设置;如果不一致,则只需要单独对此进行重新设置以覆盖定义在标准终结点的默认设置。
比如说,对于用于发布元数据的终结点总是将IMetadataExchange作为其契约,并且在大部分情况下使用MexHttpBinding。如果我们基于这两个元素创建一个标准的MexEndpoint,那么在为服务配置发布元数据的终结点的时候就只需要指定地址就可以了。实际上,WCF确实为我们创建了这么一个标准的MexEndpoint终结点。包含MexEndpoint终结点在内,WCF总共为我们定义了如下面的列表所示的9个标准终结点。
mexEndpoint:用于公开服务元数据的标准终结点;
dynamicEndpoint:使用 WS-Discovery 在运行时动态查找终结点地址的标准终结点;
discoveryEndpoint:由服务用于发送发现消息的标准终结点;
udpDiscoveryEndpoint:通过 UDP 多播绑定为发现操作预配的标准终结点;
announcementEndpoint:由服务用于发送公告消息的标准终结点;
udpAnnouncementEndpoint:由服务用于通过 UDP 绑定发送公告消息的标准终结点;
workflowControlEndpoint:可用于对工作流实例调用控制操作的标准终结点;
webHttpEndpoint:带有自动添加 WebHttpBehavior行为的WebHttpBinding绑定的标准终结点;
webScriptEndpoint:带有自动添加 WebScriptEnablingBehavior行为的WebHttpBinding绑定的标准终结点。
如果你希望直接为某个服务配置一个标准终结点,可以借助于WCF4.0为终结点的配置节添加的一个新的配置属性kind,该属性表示标准终结点名称。在上面的配置中,我为服务配置了一个标准终结点mexEndpoint以实现基于MEX终结点形式的元数据发布。
1: <?xml version="1.0"?>
2: <configuration>
3: <system.serviceModel>
4: ...
5: <services>
6: <service name="Artech.WcfServices.Service.CalculatorService" >
7: <host>
8: <baseAddresses>
9: <add baseAddress="http://127.0.0.1:3721/calculatorservice"/>
10: </baseAddresses>
11: </host>
12: <endpoint binding="ws2007HttpBinding" contract="Artech.WcfServices.Contract.ICalculator" />
13: <endpoint kind="mexEndpoint" address="mex"/>
14: </service>
15: </services>
16: </system.serviceModel>
17: </configuration>
对于系统绑定来说,WCF允许你通过配置的方式对其进行定制,标准终结点也不例外。如果标准的终结点默认配置不能满足你的要求,你可以在配置中对其进行相应的定制。在WCF配置节下添加了一个新的子结点<standardEndpoints>,用于对这9个标准终结点进行定制。和自定义绑定一样,你需要为自定义的标准终结点起一个名字。如果某个终结点需要使用到自定义的标准终结点,标准终结点的名称需要设置到终结点配置节的另一个额外的配置属性endpointConfiguration上。
在下面的配置中,我们自定义了一个基于WS-Discovery 1.1的udpDiscoveryEndpoint,并起名为“wsd11”。而这个标准终结点通过终结点配置节的两个属性kind(kind="udpDiscoveryEndpoint")和endpointConfiguration(endpointConfiguration="wsd11")被添加到寄宿的CalculatorService服务的终结点列表中。
1: <?xml version="1.0"?>
2: <configuration>
3: <system.serviceModel>
4: <services>
5: <service name="Artech.WcfServices.Service.CalculatorService" >
6: <endpoint address="http://127.0.0.1:3721/calculatorservice" binding="ws2007HttpBinding" contract="Artech.WcfServices.Contract.ICalculator" />
7: <endpoint kind="udpDiscoveryEndpoint" endpointConfiguration="wsd11"/>
8: </service>
9: </services>
10: <standardEndpoints>
11: <udpDiscoveryEndpoint>
12: <standardEndpoint name="wsd11" discoveryVersion="WSDiscovery11"/>
13: </udpDiscoveryEndpoint>
14: </standardEndpoints>
15: </system.serviceModel>
16: ...
17: </configuration>
二、无.svc文件服务激活
我们都知道,在采用IIS/WAS进行服务寄宿的情况下,我们需要为寄宿的服务创建一个.svc文件。在通常的情况下(当然你也可以以内联的形式将整个服务类型也定义其中),我们仅仅在该.svc文件中定义基本的<%@ServiceHost%>指令信息。其中最重要的指令信息自然是通过Service属性指定的寄宿服务的类型(实际上调用ServiceHostFactory的CreateServieHost方法传入的第一个参数值)。如果采用自定义ServiceHost,我们还需要定义用于创建ServiceHost的ServiceHostFactory的类型(通过Factory属性)。
在《通过自定义ServiceHost实现对WCF的扩展[实例篇]》中,我们介绍了如何通过自定义ServiceHost的方式实现WCF与Unity这个IoC框架进行集成。我们为此创建了自定义的ServiceHost(UnityServiceHost)和相应的ServiceHostFactory(UnityServiceHostFactory)。下面就是采用了UnityServiceHostFactory这个自定义ServiceHostFactory创建的.svc的内容。
1: <%@ ServiceHost Service="Artech.WcfServices.Servicies.ResourceService: defaultContainer"
2: Factory="Artech.WcfExtensions.IoC.UnityServiceHostFactory, Artech.WcfExtensions"%>
从消息交换的角度来说,客户端对IIS/WAS寄宿下服务的调用本质上体现在对.svc这个真实存在的物理文件的访问。如果服务尚未激活,WCF最终根据读取请求的物理文件来激活相应的服务。具体来说,就是获取用于创建ServiceHost的ServiceHostFactory的类型(如果没有通过<%@ServiceHost%>指令的Factory进行显式设置,默认使用的ServiceHostFactory的类型为System.ServiceModel.Activation.ServiceHostFactory)。在正确解析出ServiceHostFactory类型之后,通过反射创建用于寄宿服务的ServiceHost对象。
如果WCF的服务端能够根据请求正确地创建出基于目标服务的ServiceHost,就能解决服务的激活问题。进一步来说,如果服务端能够维护一个Service/ServiceHostFactory与请求地址之间的映射关系,我们就可以不再需要.svc文件,因为.svc对于服务激活来说就是起到了这么一个映射的作用。在最新的WCF中,这么一个映射关系可以在配置文件中进行设置。换言之,如果在配置对这个映射关系进行了相应设置之后,我们将不再需要为服务定义了.svc文件了。
在<system.serviceModel>/<serviceHostingEnvironment>配置节下,具有一个<serviceActivations>子节点。上述的关于Service/ServiceHostFactory与请求地址之间的映射关系就定义在这个配置节点下。具体来说,<serviceActivations>配置节下的配置元素具有三个基本的属性,其中service和factory对用着原来定义在.svc文件中<%@ServiceHost>指令的Service和Factory属性,而relativeAddress则表示服务相对服务寄宿的IIS站点的地址,该地址必须以.svc为后缀。下面一段配置与上面给出的.svc文件具有相同的作用,有了这段配置,.svc就不再需要了。
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <system.serviceModel>
4: ...
5: <serviceHostingEnvironment>
6: <serviceActivations>
7: <add relativeAddress="ResourceService.svc"
8: service="Artech.WcfServices.Servicies.ResourceService: defaultContainer"
9: factory="Artech.WcfExtensions.IoC.UnityServiceHostFactory, Artech.WcfExtensions"/>
10: </serviceActivations>
11: </serviceHostingEnvironment>
12: </system.serviceModel>
13: </configuration>
再举个例子,如何我们需要通过IIS的方式来寄宿我们熟悉的CalculatorService,在不需要定义.svc的情况下,下面的XML片断代表了所需的最少配置。借助于默认终结点(《[WCF 4.0新特性] 默认终结点》)的自动添加机制,WCF会为寄宿服务实现的每个服务契约针对于每一个基地址添加一个终结点。由于通过配置属性relativeAddress定义的地址就是服务的相对基地址,所以基于这个地址的终结点回自动添加。
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <system.serviceModel>
4: <serviceHostingEnvironment>
5: <serviceActivations>
6: <add service="Artech.WcfServices.Service.CalculatorService" relativeAddress="OrderService.svc"/>
7: </serviceActivations>
8: </serviceHostingEnvironment>
9: </system.serviceModel>
10: </configuration>