16.4 端点
每个WCF服务都会关联到一个用于定位服务位置的地址,一个用于定义如何与服务进行通信的绑定,以及一个告知客户端服务能做什么的合约。这三样共同组成了服务的端点。
每个端点都必须完整拥有这三个组成部分,主机通过公开端点来对外提供服务。理论上,端点就是服务的外部交互接口,就像CLR或者COM接口。每个服务至少需要公开一个端点,服务上所有的端点都必须拥有唯一的定位地址,单个服务可以提供多个端点供不同类型的客户端调用。这些端点可以使用相同或不同的绑定对象,可以拥有相同或不同的服务契约。但是对于单个服务的不同端点,它们之间没有任何关联。
下面对如何通过配置文件和编程方式定义端点进行介绍。
16.4.1 通过配置文件方式
这种方式是将端点的信息保存到主机的配置文件中,通常是app.config文件或者web.config文件。
假设使用如下代码定义了一个服务合约及其实现类。
//指定命名空间 namespace MyNameSpace { [ServiceContract] public interface IMyContract { //这里是IMyContract接口的定义 } public class MyService : IMyContract { //这里是IMyContract接口的实现 } }
如下给出了针对这个WCF服务的端点信息,其中包含服务的完整名称、绑定类型、合约的完整名称等。
<system.serviceModel> <services> <service name = "MyNameSpace.MyService"> <endpoint address = "http://localhost:8000/MyService/" binding = "wsHttpBinding" contract = "MyNameSpace.IMyContract" /> </service> </services> </system.serviceModel>
注意
在这里指定的服务名称和服务合约必须是带命名空间的完整名称,否则将无法正确引用其地址。而且地址的类型与绑定类型必须匹配,否则就会在加载服务时导致异常。
当然,也可以在配置文件中为一个单独的服务提供多个端点设置。这些端点可以使用相同的绑定类型,但是必须保证URL是唯一的。例如,如下是多个端点的示例配置文件。
<service name = "MyService"> <endpoint address = "http://localhost:8000/MyService/" binding = "wsHttpBinding" contract = "IMyContract" /> <endpoint address = "net.tcp://localhost:8001/MyService/" binding = "netTcpBinding" contract = "IMyContract" /> <endpoint address = "net.tcp://localhost:8002/MyService/" binding = "netTcpBinding" contract = "IMyOtherContract" /> </service>
还可以提供一个或多个默认的基本地址(Base Address),这样在端点设置中只需提供相对地址。多个基本地址之间不能冲突,不能在同一个端口进行监听。
相对地址通过端点绑定类型与基本地址进行匹配,从而在运行时获得完整地址。如果将某个端点设置中的地址设为空值(省略address),则表示直接使用某个相匹配的基本地址。
例如,如下的配置文件演示了这种方式。
<service name = "MyService"> <host> <baseAddresses> <add baseAddress="http://localhost:8080/" /> <add baseAddress="net.tcp://localhost:8081/" /> </baseAddresses> </host> <endpoint address = "MyService" <! -- http://localhost:8080/MyService --> binding = "wsHttpBinding" contract = "IMyContract" /> <endpoint address = "MyService" <! -- net.tcp://localhost:8081/MyService --> binding = "netTcpBinding" contract = "IMyContract" /> <endpoint address = "net.tcp://localhost:8002/MyService/" binding = "netTcpBinding" contract = "IMyOtherContract" /> </service>
此外,还可以进一步对端点中的绑定参数进行设置。每种绑定类型可拥有多个名称不同的参数设置,然后在端点的bindingConfiguration属性中指定关联设置名称。
例如,在下面的配置文件中使用bindingConfiguration属性指定关联名称为“TransactionalTCP”的绑定信息。
<system.serviceModel> <services> <service name = "MyService"> <endpoint address = "net.tcp://localhost:8000/MyService/" bindingConfiguration = "TransactionalTCP" binding = "netTcpBinding" contract = "IMyContract" /> </service> </services> <bindings> <netTcpBinding> <binding name = "TransactionalTCP" transactionFlow = "true" /> </netTcpBinding> </bindings> </system.serviceModel>
16.4.2 通过编程方式
编程方式配置端点与使用配置文件的方式是等效的。它的优点是不需要编写额外的配置文件,而是通过编程的方式将端点添加到ServiceHost实例中。如下所示为创建ServiceHost实例时可用的两个构造函数形式。
public ServiceHost(object singletonInstance, params Uri[] baseAddresses); public ServiceHost(Type serviceType, params Uri[] baseAddresses);
ServiceHost提供了一个AddServiceEndpoint()方法来向当前的服务中添加端点。如下所示为该方法的重载形式。
public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, string address); public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, Uri address); public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, string address, Uri listenUri); public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, Uri address, Uri listenUri);
在使用时address参数可以是相对地址,也可以是绝对地址,这与使用配置文件时是一致的。例如,下面的代码演示了如何通过编程方式配置端点。
ServiceHost host = new ServiceHost(typeof(MyService)); Binding wsBinding = new WSHttpBinding( ); Binding tcpBinding = new NetTcpBinding( ); host.AddServiceEndpoint(typeof(IMyContract), wsBinding, "http://localhost:8000/MyService"); host.AddServiceEndpoint(typeof(IMyContract), tcpBinding, "net.tcp://localhost:8001/MyService"); host.AddServiceEndpoint(typeof(IMyOtherContract), tcpBinding, "net.tcp://localhost:8002/MyService"); host.Open( );