WCF的全称是:Windows Communication
Foundation。从本质上来说,它是一套软件开发包,是微软公司推出的符合SOA思想的技术框架。
WCF为程序员提供了丰富的功能,其中包括:托管、服务实例管理、异步、安全、事务管理、离线队列等。并且WCF对产业中的标准协议进行了封装和定义,它把程序员从繁琐的通信、格式编码中解放出来,使得程序员能够专注于业务逻辑的实现。同时,WCF统一了微软公司之前推出的多种分布式技术,其中包括:
1.
Web服务和WSE。
2. .Net Remoting。
3. .Net 企业服务。
4.
微软消息队列(MSMQ)。
WCF对这些技术的集成包括两个方面:
1. WCF的架构本身吸取了这些技术的精华。
2.
WCF开发的服务/客户端可以和现有的Web服务、MSMQ程序进行交互。
2.2 .Net Framework的四大体系架构
.Net
Framework的四大体系包括:WCF、WF、WPF、WCS。
WCF(Windows Communication
Foundation):主要是用来做松耦合的分布式通讯的,它还有另外一个名字叫Indigo,是微软迈向SOA一个重要的标志。WCF是.Net
Framework的一个子集。
WF(Windows Wordflow Foundation):工作流引擎。
WPF(Windows
Presenttation
Foundation):WPF使用矢量绘图引擎,提供了一种声明式编程语言XAML,用来开发具有炫目视觉效果的应用程序。
WCS(Windows Card
Space):采用了一种新的数字标识技术来实现类似网银单点登录的功能,主要用来防止钓鱼式攻击等,从网络安全方面提供了一个保障。
2.3
WCF体系框架
从图上来看,WCF的体系基本包含了4个方面,分别为契约、服务运行时、消息和寄宿。
1
契约(能干什么)
从SOA的概念上来看,契约属于一个服务公开接口的一部分。一个服务的契约,定义了服务端公开的服务方法、使用的传输协议、可访问的地址、传输的消息格式等内容。
基本上,契约的定义描述了该服务的功能和作用,它告诉SOA系统中的其它节点这个服务是“干什么”的。
2
服务运行时(怎么干)
服务运行时定义了服务在运行时的具体行为。如果说契约描述了服务是“干什么”的,那么服务运行时就在一定程度上描述了服务是“怎么干”的。
3
消息
消息方面包含了消息的传输方式、消息的编码与解码。消息方面的内容基本属于服务边界以内的具体实现。具体的传递时限,必须符合在契约中定义的绑定协议。
4
激活和宿主(在哪干)
激活和宿主属于WCF程序的部署方式。一个WCF服务需要在一个可运行的程序中寄宿,我们可以把宿主理解为WCF运行的容器。常用的寄宿方式包括自寄宿、IIS寄宿、Windows激活服务、Windows服务、Com+组件等。根据SOA的原则,激活和宿主类型的变化不会影响服务本身的特性和外部对该服务的访问,而WCF在这一方面也确实做的非常出色。
2.4
WCF基础概念介绍
WCF框架中包含了大量的基础概念,本小节将以简短的篇幅带领大家浏览这些概念,使大家能够对WCF的基本概念有所了解。
1.
地址(Address)
在WCF框架中,每个服务都具有唯一的地址。在SOA系统中,其它服务和客户端通过服务的地址来对服务进行访问。一个服务的地址由一个统一资源标示符(URI)来表示。下面是几个地址示例:
http://localhost
/Service
net.tcp://dc3web1:9023/MyService
net.msmq://localhost/MyMsMqService
实际上地址的形式不止这些,它们的构成形式如下所示:
http://[Hostname]:[Port]/[ServiceAddress]
https://[Hostname]:[Port]/[ServiceAddress]
net.tcp://[Hostname]:[Port]/[ServiceAddress]
net.pipe://[Hostname]:[Port]/[ServiceAddress]
net.msmq://[Hostname]/public(private)/[QueueName]
msmq.formatname://{msmq
format name}
2.
绑定(Binging)
绑定定义了服务与外部通信的方式。它由一组称为绑定元素的元素而构成,这些元素组合在一起形成通信基础结构。一个绑定可以包含以下内容。
1.
通信所使用的协议,如HTTP、TCP、P2P等。
2. 消息编码方式,如纯文本、二进制编码、MTOM等。
3.
消息安全保障策略。
4. 通信堆栈的其它任何要素。
3.
契约(Contract)
在2.3节中,笔者已经介绍了契约的基本概念。在WCF中一共包含了4种契约,分别是服务契约、数据契约、错误契约和消息契约。
1.
服务契约[ServiceContract]
服务契约将多个相关的操作联系在一起,组成单个功能单元。
2.
数据契约[DataContract]
数据类型的说明称为数据契约。服务使用的数据类型必须在元数据中进行描述,以使其它各方面可以与该服务进行交互操作。
3.
错误契约[FaultContract]
错误类型的说明称为错误契约。
4.
消息契约[MessageContract]
消息契约描述消息的格式。
5.
终节点(EndPoint)
终结点是用来发送或接收消息(或同时执行这两种操作)的构造。一个终节点由三个要素组成,分别是笔者已经介绍了的:地址、绑定和契约。以SOA的思想来看,一个终节点就相当于服务的公共接口。
6.
元数据
服务的元数据描述服务的特征,外部实体需要了解这些特征以便与该服务进行通信。服务所公开的元数据包括XML架构文档(用于定义服务的数据协定)和WSDL文档(用于描述服务的方法)。启用元数据后,WCF通过检查服务及其终节点自动生成服务的元数据。
在WCF的行为章节中,笔者将介绍两种WCF的元数据发布方式。
7.
宿主
服务必须承载于某个进程中。宿主是控制服务的生存期的应用程序。
3
第一个WCF程序
在了解了WCF的基本概念以后,本节将按照程序员学习新技术的习惯,给出一个简单的服务契约的HelloWord实例跟一个数据契约的实例。
3.1
HelloWord服务契约的定义
using System.ServiceModel ;
[ServiceContract]
public interface IService1
{
[OperationContract]
string HelloWord(string name);
}
public
class Service1 : IService1
{
public string HelloWord(string
name)
{
return name + "说:HelloWord";
}
}
Demo1服务契约
3.2 数据契约的定义
using System.ServiceModel ;
using
System.Runtime.Serialization ;
[DataContract]
public class
People
{
[DataMember]
public string
name;
[DataMember]
public int age;
public
People(string name,int age)
{
this.name =
name;
this.age = age;
}
}
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetInfomation(People people);
}
Demo2数据契约
知识点:通过自寄宿在服务器端模拟客户端
4
WCF消息交换模式
WCF客户端与服务器之间是通过消息进行通讯,了解WCF的消息交换模式有助于大家对消息的发送和接受由更直观的理解。在WCF中,有三种消息交换模式:数据报模式、请求-响应模式、双工模式。
4.1
数据报模式(One-Way
Calls)
数据报模式指的是发送端负责把消息发送给对方并且收到确认消息之后,就完成交互的方式。在这种模式下,发送方唯一能确定的就是消息发送成功,而对于消息是否最终到达服务的终节点、是否被成功处理、发挥的结果如何都一无所知。
数据报模式存在以下特点:
1.
返回类型只能是void
2. 不能包含ref或者out类型的参数
3.
只有客户端发起请求,服务端并不会对请求进行回复。
通过设置OperationContract的IsOneWay=True可以将消息模式设置为数据报模式,具体方法如下:
[OperationContract(IsOneWay=true)]
void
Test();
Demo3数据报模式与请求-响应模式
4.2
请求-响应模式(Request/Reply)
在请求响应模式中,客户端发送一个消息并且接收一个返回消息来完成一次交互。在该模式中,消息的发起端必然是客户端,并且从服务端返回只有一条消息。客户端在发送出消息后会阻止当前线程并且等待服务端返回消息。
请求响应模式是缺省的消息交换模式,类似于HTTP协议中的请求/响应模型,这种消息交换模式是使用最多的一种。请求-响应模式有如下特点:
1.
调用服务方法后需要等待服务的消息返回
2.
在这种模式下,服务端永远是服务端,客户端就是客户端,职责分明。
它是缺省的消息交换模式,设置OperationContract便可以设置为此种消息交换模式,方法如下:
[OperationContract]
void Test();
Demo3数据报模式与请求-响应模式
4.3
双工模式(Duplex)
在双工模式中,客户端和服务端都可以任意地向对方发送消息,而对方也可以以任意的次序来接收消息。在这种模式下,发送端和接收端的概念变得不再适用,取而代之的是通信的两个端点。
这种模式相对较复杂一些,服务端的契约定义如下所示:
public interface ICallBack
{
[OperationContract(IsOneWay =
true )]
void UpdateInterval(int seconds);
}
[ServiceContract(CallbackContract=typeof(ICallBack ))]
public interface
IService1
{
[OperationContract]
void
Heartbeat();
}
Demo4双工模式
5
WCF绑定
绑定的本质是一个配置好的通道栈,为了方便程序员专注于业务逻辑,WCF提供了一系列常用的绑定。
5.1 标准绑定
在.NET
Framework 3.5中WCF一共提供了12种标准绑定,这些绑定基本能够覆盖用户所有的传输要求。
表5.1
12种标准绑定简要说明
绑定名称 简要介绍 所需.Net
Framework版本
basicHttpBinding 基于WS-I
Basic Profile 1.1的Web服务 3.0
wsHttpBinding
针对改进的Web服务的绑定,包括WS-Security,WS-Transaction等元素 3.0
wsDualHttpBinding
提供双工通信的HTTP绑定 3.0
webHttpBinding 支持REST/POX服务的绑定,使用XML/JSON序列化
3.0
netTcpBinding
使用TCP传输协议在跨主机的局域网内使用,支持可靠性、事务、安全等特性、并且该绑定被特别优化来支持WCF系统。但是,使用该绑定需要确保通信双方都基于WCF构建,这里并不符合SOA的原则
3.0
netNamedPipeBinding
支持和netTcpBinding大致相同的特性,但由于使用命名管道进行通信,所以通信不能跨越主机 3.0
netMsmqBinding
使用微软消息队列(MSMQ)协议来进行异步脱机的消息交互。关于该绑定的交互方式,在本书的后续章节中有详细的介绍
3.0
netPeerTcpBinding 使用P2P协议在网格中进行消息交互
3.0
msmqIntegrationBinding 该绑定可以用来在WCF消息和MSMQ消息中进行转换
3.0
wsFederationHttpBinding 该绑定支持使用了联合安全机制的Web服务
3.0
ws2007HttpBinding 该绑定继承自wsHttpBinding,其主要涉及目的是为了支持2007年新制定的WS标准
3.5
ws2007FederationHttpBinding
该绑定继承自wsFederationHttpBinding,和wsHttpBinding一样,其设计目的是为了支持2007年新制定的WS标准
3.5
5.2
设置绑定的方式
在WCF程序中,有两种方式来设置绑定,在代码中设置绑定和在配置文件中设置绑定。一般来说,在配置文件中设置绑定更为常用,这是因为绑定的设置常常需要根据部署环境的改变而改变。
1.
在配置文件中设置绑定
<?xml version="1.0" encoding="utf-8"
?>
<configuration>
<system.web>
<compilation
debug="true" />
</system.web>
<system.serviceModel>
<services>
<servicename="WcfServiceLibrary1.Service1"
behaviorConfiguration="WcfServiceLibrary1.Service1Behavior">
<endpoint address ="HelloWord "
binding="wsHttpBinding "
contract="WcfServiceLibrary1.IService1 ">
</endpoint>
</service>
</services>
</system.serviceModel>
</configuration>
2.
在代码中设置绑定
using System.ServiceModel;
using
System.ServiceModel.Channels;
NetNamedPipeBinding binding = new
NetNamedPipeBinding ();
EndpointAddress address = new
EndpointAddress(new Uri("net.pipe://localhost/HelloWorld "));
using (HelloWorldProxy proxy = new HelloWorldProxy())
{
Console.WriteLine(proxy.HelloWorld("Kevin"));
//利用代理调用服务
Console.Read();
}
6
WCF契约
6.1 契约的定义和分类
6.1.1
什么是契约
在2.3节中,笔者已经介绍了契约的基本概念。
契约(Contract)是 WCF
的消息标准,告知客户端如何与服务器联系交互。一般情况下,我们用接口(Interface)来定义服务契约(Service
Contract)。
6.1.2
契约的分类
在WCF中一共包含了4种契约,分别是服务契约、数据契约、错误契约和消息契约。
6.2
服务契约
在第3节中,笔者已经针对服务契约和数据契约做过两个Demo,因此服务契约和数据契约本节不做重点。
下面来看下服务契约都有哪些属性:
ServiceContract
?
ConfigurationName:其设置信息在配置文件中的名称。
? Name /
Namespace:自定义该服务契约的名称和命名空间。
? SessionMode:设置服务契约的 Session
方式,包括Allowe、NotAllowed、Required。
? CallbackContract:设置 duplex
模式时的回调类型。
?
ProtectionLevel:指定消息保护方式,可以对消息进行加密和签名处理。
OperationContract
?
AsyncPattern:用于定义异步服务方法。
? IsInitiating:指示服务方法能否启动一个 Session。
?
IsTerminating:指示服务方法调用完成是否结束 Session。
6.3
数据契约
在第3节中,笔者已经针对服务契约和数据契约做过两个Demo,因此服务契约和数据契约本节不做重点。
下面来看下数据契约都有哪些属性:
DataContract
?
Name / Namespace:自定义名称和命名空间。
DataMember
? Name:自定义名称。
?
IsRequired:指示该成员序列化前必须被赋值。
6.4
错误契约
在WCF中消息首先会被序列化为SOAP消息再进行发送。也就是说,所有的数据都包含在SOAP消息中,包括异常信息。SOAP规定了异常消息的携带方式,那就是全部放入Fault节点中。Fault节点必须是Body节点的子节点,同时,一个SOAP消息中只能出现一个Fault节点。
表6.1
Fault的子节点
子节点 描述
<Code>
供识别故障的代码,可包含两个子节点:Code和Subcode
<Reason>
可供人阅读的有关故障的说明
<Node> 可选,指向SOAP消息中引发错误的节点
<Role>
可选,发生错误时节点所处的角色
<Detail>
可选,包含了详细的错误信息
说明:无论是使用哪种错误方式:WCF系统最终会把错误信息放入Fault节点中进行传输。
6.4.1
WCF中默认的异常处理
WCF 将服务异常(Exception)转换成 SOAP faults,传递到客户端后再次转换成
Exception。只不过缺省情况下,我们很难从中获取有意义的信息。
[ServiceContract]
public
interface IService1
{
[OperationContract]
int
Add(int a, int b);
}
public class Service1 : IService1
{
public int Add(int a, int b)
{
throw new
Exception("错误");
}
}
异常信息如下所示:
Unhandled Exception:
System.ServiceModel.FaultException: The server was unable t
o process the
request due to an internal error. For more information about the
error,
either turn on IncludeExceptionDetailInFaults (either from
ServiceBehavio
rAttribute or from the <serviceDebug> configuration
behavior) on the server in o
rder to send the exception information back to
the client, or turn on tracing as
per the Microsoft .NET Framework 3.0 SDK
documentation and inspect the server t
race logs.
Server stack
trace:
at
System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message
reply,
MessageFault fault, String action, MessageVersion version, FaultConverte
r
faultConverter)
at
System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRunt
ime
operation, ProxyRpc&rpc)
at
System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean
on
eway, ProxyOperationRuntime operation, Object[] ins, Object[] outs,
TimeSpantim
eout)
at
System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean
on
eway, ProxyOperationRuntime operation, Object[] ins, Object[] outs)
at
System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCall
Message
methodCall, ProxyOperationRuntime operation)
at
System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage
message)
Exception rethrown at [0]:
at
System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage
req
Msg, IMessageretMsg)
at
System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData&msgDa
ta,
Int32 type)
at
ConsoleClient.ServiceReference1.IService1.TestException()
at
ConsoleClient.ServiceReference1.Service1Client.TestException() in
e:wcf讲
稿(kevin)demo-kevindemo5wcf中默认异常处理consoleclientservice
referencess
ervicereference1reference.cs:line 50
at
ConsoleClient.Program.Main(String[] args) in
E:WCF讲稿(Kevin)Demo-Kevi
nDemo5WCF中默认异常处理ConsoleClientProgram.cs:line
13
请按任意键继续. . .
6.4.2 WCF中的FaultException
我们将代码改为如下:
public void TestException()
{
throw new Exception(new
FaultException("异常测试").ToString());
}
这次输出信息要有好的多,异常信息如下所示:
Unhandled Exception:
System.ServiceModel.FaultException: 异常测试
Server stack trace:
at
System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRunt
ime
operation, ProxyRpc&rpc)
at
System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean
on
eway, ProxyOperationRuntime operation, Object[] ins, Object[] outs,
TimeSpantim
eout)
at
System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean
on
eway, ProxyOperationRuntime operation, Object[] ins, Object[] outs)
at
System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCall
Message
methodCall, ProxyOperationRuntime operation)
at
System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage
message)
Exception rethrown at [0]:
at
System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage
req
Msg, IMessageretMsg)
at
System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData&msgDa
ta,
Int32 type)
at Client.ServiceReference1.IService1.Add(Int32 a, Int32
b)
at Client.ServiceReference1.Service1Client.Add(Int32 a, Int32 b) in
e:wcf讲
稿(kevin)demo异常处理异常处理clientservice
referencesservicereference1re
ference.cs:line 50
at
Client.Program.Main(String[] args) in
E:WCF讲稿(Kevin)Demo异常处理异
常处理ClientProgram.cs:line 13
请按任意键继续. .
.
6.4.3
WCF中的FaultContractAttribute
另外,我们可以通过FaultContractAttribute传递更详细的异常信息给客户端。
代码如下所示:
[ServiceContract]
public interface IService1
{
[OperationContract]
[FaultContract(typeof(FaultMessage))]
int Add(inta,int b);
}
[DataContract]
public class
FaultMessage
{
[DataMember]
public string
Message;
[DataMember]
public interrorCode;
}
public class Service1 : IService1
{
public int Add(int a, int
b)
{
FaultMessagefaultMessage = new
FaultMessage();
faultMessage.Message = "错误信息";
faultMessage.errorCode = 1234;
throw new
FaultException<FaultMessage>(faultMessage,
faultMessage.Message);
}
}
客户端的异常信息
Unhandled Exception:
System.ServiceModel.FaultException`1[Client.ServiceReferenc
e1.FaultMessage]:
错误信息 (Fault Detail is equal to
Client.ServiceReference1.Fa
ultMessage).
注意:由于单程操作没有任何返回值,也不回把任何错误发还给客户端,所以不能在单程操作上使用错误契约,不然会导致运行时异常。
6.5
消息契约
使用数据契约已经足以应付消息交互双方对数据内容的所有要求,但是对消息的格式却无法全面控制。如果想要定制SOAP消息的Head和Body内容,这就需要定制SOAP消息的Head和Body内容,这就需要用到消息契约。消息契约在实际WCF编程中使用的并不多,本节不做详细讨论,只给出一个简单的示例:
[MessageContract ]
public interface MyMessage
{
[MessageHeader ]
public intSessionId;
[MessageHeader]
public string Description;
[MessageBodyMember ]
public string A;
[MessageBodyMember]
public string B;
[MessageBodyMember]
public string
C;
}
说明:消息中包含Header跟Body两部分。Header用来存放上下文信息,Body用来存放数据信息。
7
WCF中的行为
行为(Behaviors)指的是那些影响WCF系统运行的设置。行为在宿主和客户端启动后发挥作用,WCF系统中行为包括服务行为(Service
Behavior)和操作行为(Operation
Behavior)。服务行为作用在终节点上,常见的服务行为包括实例控制、并发控制、元数据发布等。操作行为作用于某一服务操作上,常见的操作行为包括事务流设置等。
7.1
实例控制
当服务端接收到客户端的调用之后,需要把该消息发送到具体的服务实例上。实例管理,就是和所有实例的分配、管理有关的技术统称。实例管理属于服务行为的一种,本节将简要介绍下实例管理。
7.1.1
实例管理的设置
所有的服务行为都通过ServiceBehavior特性来进行设置,实例管理业不例外。实例管理通过ServiceBehavior特性的InstanceContextMode属性进行设置,该属性的类型为InstanceContextMode枚举类型。枚举类型定义如下:
public enumInstanceContextMode
{
PerSession = 0 ,
PerCall = 1 ,
Single = 2
}
下面将通过一个例子来演示如何为服务类型配置实例策略
using
System.ServiceModel;
[ServiceContract]
public interface
IService1
{
[OperationContract]
string
HelloWord(string name);
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession )]
public class Service1 : IService1
{
public string
HelloWord(string name)
{
return name +
"说:HelloWord";
}
}
7.1.2
PerSession实例策略
PerSession是默认的实例策略,PerSession代表了会话的实例管理模式。会话管理模式相对较为负责,仅仅设置服务行为是不够的,需要满足一下3个方面才能在客户端和服务端之间建立会话。这3个方面是:
1.
服务契约的会话设置
2. 服务行为的实例模式设置
3. 绑定的选择
7.1.2.1
服务契约的会话设置
服务契约的会话设置通过ServerContract的SessionMode属性进行设置,SessionMode属性是枚举类型,SessionMode定义如下:
public enumSessionMode
{
Allowed = 0,
Required = 1,
NotAllowed = 2
}
服务契约的SessionMode设置如下所示:
[ServiceContract(SessionMode=SessionMode.Allowed)]
public interface
IService1
{
[OperationContract]
string
HelloWord(string name);
}
7.1.2.2
服务行为的实例模式设置
这里没有太多可讨论的话题,为了使用会话模式,实例模式必须设置为PerSession。
7.1.2.3
绑定的选择
绑定的选择需要注意的就是绑定是否支持会话,如basicHttpBinding则无法应用于会话实例管理模式。
7.1.3
PerCall实例策略
当服务端采用了PerCall实例策略后,每个客户端的请求消息都会被分发到一个新的服务实例上。而一旦这个调用返回后,服务实例则会被销毁。
7.1.4
Single实例策略
Single实例策略非常类似设计模式中的单件模式,所有的客户端代理发送的消息都会被应用到同一个服务实例上。
7.2
并发控制
并发的概念也可以理解为多线程,在WCF中,并发也属于一种服务行为。
7.2.1
并发管理的设置
和实例管理一样,并发管理业通过服务行为的属性来设置。并发管理的属性名为ConcurrencyMode,它的类型是枚举类型,该定义类型如下:
public enumConcurrencyMode
{
Single =
0,
Reentrant = 1,
Multiple = 2
}
7.2.2
Single并发模式
ConcurrentMode.Single并发模式是默认的并发设置,当设置了Single模式以后,WCF会为服务实例的操作提供同步锁。Single并发模式的策略较为极端,等同于在整个操作上加上了同步锁,在很多情况下,这并不是必须的。
7.2.3
Multiple并发模式
ConcurrentMode.Multiple表示WCF不会主动为服务操作添加任何锁,每个操作都允许客户端多个请求同时访问,这样做的好处是提高了系统的运行效率,防止消息被阻塞。
7.2.4
Reentrant并发模式
本质上,ConcurrentMode.Reentrant与ConcurrentMode.Single模式一样:在同一时间只允许一个线程访问服务操作,在访问进入操作之前必须获得同步锁。所不同的是,ConcurrentMode.Reentrant模式解决了ConcurrentMode.Single模式的死锁问题。
实际上ConcurrentMode.Reentrant模式的设计正是为了解决死锁问题。当服务操作在调用其它服务,或者回调客户端操作时,会释放服务端的同步锁,这样就能够保证在回调消息返回或者调用链回到服务端时不会发生死锁。在一些消息量较大,服务操作中较长时间访问其它操作的情况下,ConcurrentMode.Reentrant模式能够有效的保证配对消息能够在服务操作向外调用时进入操作。
7.3
元数据发布
在以前的小节中,笔者已经介绍了关于元数据的概念。
在WCF中,为了使客户端获得这些元数据并且知道如何访问服务,宿主程序往往会采用某种方式来发布服务的元数据,本节将讨论具体元数据的发布技术。
7.3.1
HTTP-GET方式发布元数据
下面的配置文件展示了如何配置元数据行为来允许客户端使用HTTP-GET方式获取元数据
<?xml
version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<compilation debug="true" />
</system.web>
<system.serviceModel>
<services>
<service name="WcfServiceLibrary1.Service1"
behaviorConfiguration="WcfServiceLibrary1.Service1Behavior">
<host>
<baseAddresses>
<add
baseAddress =
"http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary1/Service1/"
/>
</baseAddresses>
</host>
<endpoint address ="" binding="wsHttpBinding"
contract="WcfServiceLibrary1.IService1">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint
address="mex" binding="mexHttpBinding"
contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior
name="WcfServiceLibrary1.Service1Behavior">
<serviceMetadatahttpGetEnabled="True "/>
<serviceDebugincludeExceptionDetailInFaults="False" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
当启动了HTTP-GET的元数据获取方式后,可以尝试通过浏览器来查看服务端的元数据。在地址栏输入http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary1/Service1/?wsdl
就可以得到下图的结果:
说明:除了在配置文件中配置元数据行为,还可以通过程序设置是否启用HTTP-GET,但是笔者不推荐大家这样去做。在此只是想让大家知道有这一做法。
7.3.2
MEX终节点方式发布元数据
MEX终节点方式发布元数据配置起来也很简单,下面请看一个配置文件:
<?xml version="1.0"
encoding="utf-8" ?>
<configuration>
<system.web>
<compilation debug="true" />
</system.web>
<system.serviceModel>
<services>
<service
name="WcfServiceLibrary1.Service1"
behaviorConfiguration="WcfServiceLibrary1.Service1Behavior">
<host>
<baseAddresses>
<add
baseAddress =
"http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary1/Service1/"
/>
</baseAddresses>
</host>
<endpoint address ="" binding="wsHttpBinding"
contract="WcfServiceLibrary1.IService1">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint
address="mex" binding="mexHttpBinding"
contract="IMetadataExchange"/>
<endpoint address="mex"
binding="mexTcpBinding" contract="IMetadataExchange"/>
<endpoint address="mex" binding="mexNamedPipeBinding"
contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior
name="WcfServiceLibrary1.Service1Behavior">
<serviceMetadatahttpGetEnabled="True"/>
<serviceDebugincludeExceptionDetailInFaults="False" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
当配置好MEX终节点以后,就可以再VS2008中的客户端,点击Add
Service
Reference…,然后输入元数据的地址,来获取服务器端的元数据。本例中的通过mexHttpBinding访问的元数据地址为http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary1/Service1/mex,tcp跟namepipe的访问地址需要根据实际情况来进行构造。具体构造方法请参照以前章节的绑定。