对服务操作来说,同步请求回复消息交换是最普通的模式。这个模式就像任何人在面向过程或者面向对象语言中编程的那样。请求回复模式是本地过程调用的原型,对远程过程调用也很普通。图片2.3显示了一个请求回复交互,一个在客户端运行的代理发送请求给一个服务,服务端同步返回消息给客户端。
WCF使得在客户端和服务端进行请求-回复通信非常容易。在设计阶段,你使用添加服务引用或者svcutil.exe来调用服务元数据终结点而且生成一个客户端代理来模仿服务操作的签名。这允许客户端代码像本地函数调用一样调用代理上的方法。代理把方法名字和参数序列化成一个SOAP消息,然后发送SOAP消息到服务端,然后创建一个.NET类型来表示从服务返回的回复消息。
列表2.1显示一个服务契约定义。一个服务契约和一个操作契约被定义到代码中。操作契约代表一个可以被客户端调用的方法,或者更准确一些的说,一条消息可以被客户端发送并被服务端理解。注意契约在接口中定义,而不是类定义。
using System; using System.ServiceModel; namespace EssentialWCF { [ServiceContract] public interface IStockService { [OperationContract] double GetPrice(string ticker); } public class StockService : IStockService { public double GetPrice(string ticker) { return 94.85; } } }
列表2.2显示了客户端代码,使用一个由添加服务引用生成的代理并调用列表2.1中的服务。这很像列表1.2中显示的代码。
using System; using System.ServiceModel; namespace Client { class Client { static void Main(string[] args) { localhost.StockServiceClient proxy = new localhost.StockServiceClient(); double price = proxy.GetPrice("msft"); Console.WriteLine("msft:{0}", price); proxy.Close(); } } }
列表2.3 显示了从客户端发送到服务终结点的SOAP消息。有很多值得注意的地方:
SOAP消息的命名空间是http://tempuri.org/,除非在[ServiceContract]中重载属性否则这就是默认的。如果服务将要暴露在一个应用的外面或者在一个相对小的组织外面,你应该重载默认属性,因为命名空间结构设计来唯一定义你的服务来避免多个连接服务模棱两可。
SOAP消息的命名空间是http://tempuri.org/,除非在[ServiceContract]中重载属性否则这就是默认的。如果服务将要暴露在一个应用的外面或者在一个相对小的组织外面,你应该重载默认属性,因为命名空间结构设计来唯一定义你的服务来避免多个连接服务模棱两可。
在列表1.1中类的定义中的方法名,GetPrice用来定义wsa:动作在SOAP消息头中。完全的动作名是契约命名空间的组合,契约名字(接口名字或者类型,如果没有明确的服务接口被使用)操作名字,一个额外的字符串(如果消息是一个相关的回复)。
SOAP消息体由方法签名控制,具体方法由[OperationContract]和[DataContract]属性确定。SOAP消息头包括消息将要发送到的地址。这种情况下,它是寄宿在IIS服务器上的SVC文件。
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Header> <To s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none"> </To> <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none"> http://tempuri.org/StockService/GetPrice </Action> </s:Header> <s:Body> <GetPrice xmlns="http://tempuri.org/"> <ticker>msft</ticker> </GetPrice> </s:Body> </s:Envelope>