[WCF编程]4.契约概述
一、契约的基本概念
契约是消息参与者之间的约定。在SOA架构中,契约提供了服务通信所必需的元数据。契约用来定义数据类型,操作,消息交换模式和消息交换使用的传输协议。契约通常是在标准化平台中使用与编程语言无关的XML格式发布的。这样做的好处是:允许契约涉及的各方都能够使用和理解契约。在WCF中,服务元数据通常是用WSDL(Web服务描述语言)和XSD(扩展样式定义)文档格式来描述的。在WCF程序中,契约不一定是WSDL和XSD的文档集合,而可能是.NET类型定义的集合,如有需要就可以转化为WSDL和XSD的格式文档。
WCF契约是标注了特定特性的.NET类型,通过这些特定来产生符合行业标准的WSDL和XSD文档。WCF契约会把这些类型映射为服务、操作、消息和消息中的部分。WCF有5种类型的契约:服务契约、操作契约、数据契约、消息契约和错误契约。服务契约映射类型的服务定义,并且映射类型成员到服务操作;数据契约和消息契约映射类型到服务操作的消息定义。与数据契约相比,消息契约提供了对整个消息定义的控制。数据契约会映射为消息类型的消息体成员,而消息契约会映射为消息类型的消息头和消息体成员。
二、服务契约
服务契约描述的是一个服务,它定义了 服务向外界公开的功能,这些功能表现为服务操作(Service Operation)。服务契约包括服务定义的各个方面、服务的操作、每个操作的消息交换模式以及每个操作使用的消息。
创建服务契约的第一步就是建立操作的名字和使用的消息交换契约模式。以《[WCF编程]1.WCF入门示例》中为例,服务契约包含三个操作: SayHello、Say和SayOneWay.假设,SayHello和Say使用请求/应答消息交换模式,而SayOneWay使用数据报(单向)消息交换模式,则服务可以定义如下:
public interface IHelloWorldService
{
[OperationContract()] string SayHello(string str); [OperationContract]
Student Say(); [OperationContract(IsOneWay=true)]
void SayOneWay(string str);
}与普通的.NET接口定义的差别在于ServiceContractAttribute和OperationContractAttribute。ServiceContractAttribute标注的作用就是告诉WCF基础结构,可以使用该接口作为服务契约。OperationContractAttribute则意味着该方法是服务中的一个操作。
ServiceContractAttribute和OperationContractAttribute类型定义了几个实例属性,当在服务里使用这些属性时,他们会提供对服务契约的控制。
ServiceContractAttribute的类型定义如下:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
public sealed class ServiceContractAttribute : Attribute
{
public ServiceContractAttribute();
public Type CallbackContract { get; set; }
public string ConfigurationName { get; set; }
public bool HasProtectionLevel { get; }
public string Name { get; set; }
public string Namespace { get; set; }
public ProtectionLevel ProtectionLevel { get; set; }
public SessionMode SessionMode { get; set; }
}CallbackContract属性可以在定义双工契约时使用。ConfigurationName属性是配置文件中引用服务的简称。Name和Namespace属性是服务的名称和命名空间,这些是都会序列化到服务的XML数据名称和命名空间中。
ProtectionLevel属性
ProtectionLevel属性表示当使用一个服务契约时,绑定必须达到的消息安全级别。该属性的类型为System.Net.Security.ProtectionLevel,它的三个枚举值是:None,Sign和EncryptAndSign。当System.Net.Security.ProtectionLevel属性的值为Sign时,服务发送和接受的所有消息必须经过签名。当属性设置为None时,表示服务发送和接受的消息无须启用消息安全。ProtectionLevel属性影响的是是消息体中数据,不会影响消息头的结构。
SessionMode属性
SessionMode属性表示程序里使用的通道必须、允许或者不允许使用会话通道。SessionMode属性是System.ServiceModel.SessionMode类型,它的枚举值是Allowed-允许会话、NotAllowed-不允许会话、Required-要求会话(需要有支持会话的Binding支持,WsHttpBinding、NetTcpBinding等)。若要设计一个会话契约,我们建议使用Required 方式,而不是默认值Allowed。SessionMode.Required值要求必须使用传输层会话,但应用层会话却不是必要的,如果服务终结点的绑定没有维持一个传输层会话,就不能为这样的服务契约配置SessionMode.Required。这一约束条件会在装载服务时进行验证。但是,我们仍然可以将服务配置为单调服务,服务实例会在每次客户端调用期间创建与销毁实例。只有当服务被配置为会话服务时,服务实例才会存活于整个客户端会话中。
三、操作契约
服务契约包含服务中的描述信息。当描述服务契约中的操作时,有必要先描述操作的消息交换模式、操作接受的消息结构和操作发送的消息结构。如下示例:
[ServiceContract(Namespace="www.cnblogs.com")]
public interface IHelloWorldService
{
[OperationContract()]
string SayHello(string str); [OperationContract]
Student Say(); [OperationContract(IsOneWay=true)]
void SayOneWay(string str);
}OperationContractAttribute标注也包含几个可以控制消息交换模式、安全、会话和消息结构的属性。OperationContractAttribute只在方法上有效。
下面是OperationContractAttribute公开的API:
[AttributeUsage(AttributeTargets.Method)]
public sealed class OperationContractAttribute : Attribute
{
public string Action { get; set; }
public bool AsyncPattern { get; set; }
public bool HasProtectionLevel { get; }
public bool IsInitiating { get; set; }
public bool IsOneWay { get; set; }
public bool IsTerminating { get; set; }
public string Name { get; set; }
public ProtectionLevel ProtectionLevel { get; set; }
public string ReplyAction { get; set; }
}AsyncPattern属性
AsyncPattern属性表示操作是否是异步编程模型(APM)的一部分。当这个属性设置为true时,该属性必须应用到Begin/End对中的Begin<方法名>上。End<方法名>不需要使用该属性。如果没有End<方法名>,则契约就不会被使用。当AsyncPattren属性设置为true时,接受基础结构就会异步地调用Begin<方法名>。操作中执行I/O的接收程序要把这个属性设置为true,因为这样可以带来更多的伸缩性。如果一个操作只单纯地执行计算任务,那么该属性就没有必要设为true,因为这会导致一些性能问题。AsyncPattern属性对于发送者来说是完全透明的。
如下所示:
IHelloWorldService(接口):
[ServiceContract(Namespace="www.cnblogs.com")]
public interface IHelloWorldService
{
[OperationContract(AsyncPattern = true)]
IAsyncResult BeginRead(string fileName, AsyncCallback userCallback, object stateObject); string EndRead(IAsyncResult asynResult);
}HelloWorldService(具体类):
public class HelloWorldService : IHelloWorldService
{
private const string baseLocation = @"E:\";
private FileStream _stream;
private byte[] _buffer; public IAsyncResult BeginRead(string fileName, AsyncCallback userCallback, object stateObject)
{
this._stream = new FileStream(baseLocation + fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
this._buffer = new byte[this._stream.Length];
return this._stream.BeginRead(this._buffer, 0, this._buffer.Length, userCallback, stateObject);
} public string EndRead(IAsyncResult ar)
{
this._stream.EndRead(ar);
this._stream.Close();
return Encoding.ASCII.GetString(this._buffer);
}
}HelloWorldServiceClient(客户端代理类):
public partial class HelloWorldServiceClient : System.ServiceModel.ClientBase<IHelloWorldService>, IHelloWorldService
{ ......
public string Read(string fileName)
{
return base.Channel.Read(fileName);
} public System.Threading.Tasks.Task<string> ReadAsync(string fileName)
{
return base.Channel.ReadAsync(fileName);
}
}客户端生成的服务契约和服务代理类中,会有一个唯一的操作Read。也就是说,不管服务采用同步模式还是异步模式实现,对客户端的服务调用方式没有任何影响,客户端可以任意选择相应的模式进行服务调用。
Program(客户端)class Program
{
static void Main(string[] args)
{
string s = client.Read("示例"); Console.Read();
}
}IsOneWay属性
默认情况下,所有的操作都使用请求/应答消息交换模式。咋一看,要创建一个单向操作,只要把操作的返回值类型定义为void就可以了。事实上,在接受程序里,如果方法返回被定义为void,则程序会产生一个应答消息,并且这个应答消息的消息体不会包含任何信息。如果想使用数据报(单向)消息交换模式,则在这个方法上再设置IsOneWay属性为ture。推荐大家使用这种交换模式,因为数据包消息交换模式能带来更高的伸缩性,也能满足更高级场合的使用。
显然,错误处理更加适用于请求/应答消息交换模式而不是数据报消息交换模式,这也是开发团队选择选择请求/应答交换模式作为默认交换模式的原因之一。假如出现一个错误,则接受者通过回发通道发送一个错误消息给发送者。在契约中,来自数据报消息交换模式的错误必须通过WS-Addressing FaultTo消息头节点中指定的地址发送给发送者。但是出于安全的原因,默认情况下不会启用这些行为。
IsInitiating和IsTerminating属性
IsInitiating和IsTerminating属性会影响终结点的会话行为。如果IsInitiating属性的值为true,操作接收到一个新消息就会重新启动一个新的会话。如果IsTerminating属性的值为true,操作接收到一个消息时就会终止当前会话。操作可以同时将IsInitiating和IsTerminating属性的值设置为true。只有当ServiceContractAttribute上的SessionMode属性设置为Required时,这些属性才可以设置为true。IsInitiating和IsTerminating属性绝大多数情况下会在会话开始和结束的服务里使用。
Name、Action和ReplyAction属性
Name属性可以获取或设置操作的名称。默认情况下,该属性就是接口或类中的方法名。Action属性让WS-Addressing的action与接受到的消息关联起来,而ReplyAction属性可以把WS-Addressing的action与应答消息关联起来。如果Action属性的值设置为*,则这个操作就可以成为任何一个WS-Addressing消息。这个功能十分有用,尤其是当一个操作要接受各种不同消息时,如消息路由。
四、数据契约
数据契约定义了客户端与服务之间信息交换的结构和内容。数据契约会把.NET类型映射到消息体上,而且它是消息序列化和反序列化的关键部分。数据契约可以单独使用,但通常都是在服务契约的操作中使用。数据契约中重要的特性就是DataContractAttribute和DataMemberAttribute 。示例如下所示:
[DataContract]
public class Student
{
[DataMember]
public string UserName { get; set; }
[DataMember]
public int Age { get; set; }
[DataMember]
public GenderMode Gender { get; set; } }DataContractAttribute特性
DataContractAttribute只能用于枚举、类和结构体,而不能用于接口;DataContractAttribute是不可以被继承的,也就是说当一个类型继承了一个应用了DataContractAttribute特性类型,自身也只有显式地应用DataContractAttribute特性才能成为数据契约;一个类型上只能应用唯一一个DataContractAttribute特性。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum, Inherited = false, AllowMultiple = false)]
public sealed class DataContractAttribute : Attribute
{
public DataContractAttribute();
public bool IsReference { get; set; }
public string Name { get; set; }
public string Namespace { get; set; }
}DataContractAttribute仅仅包含3个属性成员。其中Name和Namespace表示数据契约的名称和命名空间;IsReference表示在进行序列化的时候是否保持对象现有的引用结构。比如说,一个对象的两个属性同时引用一个对象,那么有两个序列化方式,一种是在序列化后的XML仍然保留这种引用结构,另一种是将两个属性的值序列化成两份独立的具有相同内容的XML。
DataMemberAttribute特性
DataMemberAttribute特性只能应用类成员,只有应用了DataMemberAttribute特性的字段或者属性成员才能成为数据契约的数据成员。
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class DataMemberAttribute : Attribute
{ public DataMemberAttribute();
public bool EmitDefaultValue { get; set; }
public bool IsRequired { get; set; }
public string Name { get; set; }
public int Order { get; set; }
}Name:数据成员的名称,默认为字段或者属性的名称;
Order:相应的数据成员在最终序列化后的XML出现的位置,Order值越小越靠前,默认值为-1;
IsRequired:表明属性成员是否是必须的成员,默认值为false,表明该成员是可以缺省的;
EmitDefaultValue:表明在数据成员的值等于默认值的情况下,是否还需要将其序列化到最终的XML中,默认值为true,表示默认值会参与序列化。数据契约和数据成员只和是否应用了DataContractAttribute和DataMemberAttribute有关,与类型和成员的存取限制修饰符(public,internal、protected,private等)无关。也就是说,应用了DataMemberAttribute的私有字段或属性成员也是数据契约的数据成员。
五、消息契约
消息契约属于高级功能,使用它可以对SOAP头和SOAP体进行更好的控制。此外,消息契约提供了序列化期间进行安全机制的功能。所有的消息契约必须实现一个公开的、无参构造函数。
消息契约使用的标注特性为MessageContractAttribute、 MessageHeaderAttribute、MessageBodyMemberAttribute。示例代码如下:
[MessageContract]
public class TestMessage
{
[MessageHeader]
public string Header { get; set; }
[MessageBodyMember]
public string Body { get; set; }
}标注了MessageHeaderAttribute的类成员将会被序列化到消息头中。将类成员放到消息头中的首要原因就是让基础消息结构可以访问这个属性。
MessageContractAttribute特性
通过在一个类或者结构(Struct)上应用MessageContractAttribute使之成为一个消息契约
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false)]
public sealed class MessageContractAttribute : Attribute
{
public MessageContractAttribute();
public bool HasProtectionLevel { get; }
public bool IsWrapped { get; set; }
public ProtectionLevel ProtectionLevel { get; set; }
public string WrapperName { get; set; }
public string WrapperNamespace { get; set; }
}ProtectionLevel和HasProtectionLevel:表示保护级别,在服务契约中已经对保护级别作了简单的介绍,WCF中通过System.Net.Security.ProtectionLevel枚举定义消息的保护级别。一般有3种可选的保护级别:None、Sign和EncryptAndSign;
IsWrapped、WrapperName、WrapperNamespace:IsWrapped表述的含义是是否为定义的主体成员(一个或者多个)添加一个额外的根节点。WrapperName和WrapperNamespace则表述该根节点的名称和命名空间。IsWrapped、WrapperName、WrapperNamespace的默认是分别为true、类型名称和http://tempuri.org/。MessageHeaderAttribute特性
MessageHeaderAttribute用于定义消息报头成员
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
public class MessageHeaderAttribute : MessageContractMemberAttribute
{
public MessageHeaderAttribute();
public string Actor { get; set; }
public bool MustUnderstand { get; set; }
public bool Relay { get; set; }
}Actor:表示处理该报头的目标节点(SOAP Node),SOAP1.1中对应的属性(Attribute)为actor,SOAP 1.2中就是我们介绍的role属性;
MustUnderstand:表述Actor(SOAP 1.1)或者Role(SOAP 1.2)定义的SOAP节点是否必须理解并处理该节点。对应的SOAP报头属性为mustUnderstand;
Relay:对应的SOAP报头属性为relay,表明该报头是否需要传递到下一个SOAP节点。MessageBodyMemberAttribute特性
MessageBodyMemberAttribute用于定义消息主体成员
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = false)]
public class MessageBodyMemberAttribute : MessageContractMemberAttribute
{
public MessageBodyMemberAttribute();
public int Order { get; set; }
}MessageBodyMemberAttribute的定义显得尤为简单,仅仅具有一个Order对象,用于控制成员在SOAP消息主体中出现的位置。默认的排序规则是基于字母排序。
[WCF编程]4.契约概述的更多相关文章
- [WCF编程]5.绑定概述
一.绑定概述 WCF提供了一个编程框架,可以抽象化服务创建的复杂过程.绑定允许开发人员将精力集中在问题本身上,而无需考虑如何创建允许系统运行的架构,因为WCF已经创建了架构. 绑定类型是开发人员控制W ...
- [WCF编程]2.SOA概述
一.SOA简介 1. SOA(面向服务架构)既是一种编程方式,也是软件开发的一种架构方法.根据这种架构方法,应用程序是由具有一定行为(称为服务)的功能单元组成的. 2. SOA的基本思想是构建一个粗粒 ...
- 【WCF--初入江湖】01 WCF编程概述
01 WCF编程概述 SOA的优点 1.服务独立于平台和工作环境.服务并不关心自己所处的环境,也不关心与之进行通信的服务所处的 环境. 2.服务相互隔离. 3.服务对协议.格式和传输中立. 4. ...
- .Net-WCF-图书:《WCF编程》
ylbtech-.Net-WCF-图书:<WCF编程> <WCF编程>是2008年1月机械工业出版社出版的图书,作者是Juval Lowy.Clemens Vasters. 1 ...
- WCF 编程实验室
最近由于项目需要,简单研究了一下.NET WCF编程. 首先,简单说下WCF是什么,WCF 本质上,是一种开发框架.它用来开发类似COM+ .WEB SERVICE 这样“远程方法调用” 功能. 普通 ...
- WCF编程系列(七)信道及信道工厂
WCF编程系列(七)信道及信道工厂 信道及信道栈 前面已经提及过,WCF中客户端与服务端的交互都是通过消息来进行的.消息从客户端传送到服务端会经过多个处理动作,在WCF编程模型中,这些动作是按层 ...
- WCF编程系列(六)以编程方式配置终结点
WCF编程系列(六)以编程方式配置终结点 示例一中我们的宿主程序非常简单:只是简单的实例化了一个ServiceHost对象,然后调用open方法来启动服务.而关于终结点的配置我们都是通过配置文件来 ...
- WCF编程系列(四)配置文件
WCF编程系列(四)配置文件 .NET应用程序的配置文件 前述示例中Host项目中的App.config以及Client项目中的App.config称为应用程序配置文件,通过该文件配置可控制程序的 ...
- WCF编程系列(五)元数据
WCF编程系列(五)元数据 示例一中我们使用了scvutil命令自动生成了服务的客户端代理类: svcutil http://localhost:8000/?wsdl /o:FirstServic ...
随机推荐
- 使用JavaScript序列化任意复杂的对象
在phonegap的开发中,有时需要知道对象的所有属性,就简单的写了个序列化的方法. 序列化方法如下: function serialize(obj, name) { var result = &qu ...
- 浅析UPnP协议
摘要:文章介绍了UPnP结构规范和开发流程,指出:UP nP协议使所有联网的设备实现互联,设备工作不需要传统的驱动程序,便可以实现设备间的 相互控制. 关键词:UPnP协议:开发流程:电子技术:工作 ...
- 七天学会ASP.NET MVC (一)——深入理解ASP.NET MVC
系列文章 七天学会ASP.NET MVC (一)——深入理解ASP.NET MVC 七天学会ASP.NET MVC (二)——ASP.NET MVC 数据传递 七天学会ASP.NET MVC (三)— ...
- Azure PowerShell (7) 使用CSV文件批量设置Virtual Machine Endpoint
<Windows Azure Platform 系列文章目录> 请注意: - Azure不支持增加Endpoint Range - 最多可以增加Endpoint数量为150 http:// ...
- Optimistic Concurrency VS. Pessimistic Concurrency Control
原创地址:http://www.cnblogs.com/jfzhu/p/4009918.html 转载请注明出处 (一)为什么需要并发控制机制 并发控制机制是为了防止多个用户同时更改同一条数据,也 ...
- fir.im Log Guru 正式开源,快速找到 iOS 应用无法安装的原因
很开心的宣布 Log Guru 正式开源! Log Guru,是 fir.im 开发团队创造的小轮子,用在 Mac 电脑上的日志获取,Github 地址:FIRHQ/LogGuru. Log Guru ...
- Python 学习之路 (前言)
为什么要学Python 1,脚本语言本身很方便简洁,未来会有趋势 2,web 方向 3,运维方向 我是学静态语言出身的,java,毕业后从事android 应用开发,曾在工作期间学习过linux,想 ...
- ScriptTask读取SharePoint上的Excel
很多情况下,我们会SharePoint 作为文件共享的Server,有时,业务上需要将SharePoint上的Excel文档 Download 到db中.对于搞DB的人来说,难点是如何下载Excel? ...
- Power BI官方视频(1) Power BI Desktop 7月份更新功能概述
2016年7月,Power BI Desktop进行了一些功能更新,提高整体的用户体验.同时也有一些新的和令人兴奋的功能.看看大概介绍,更新功能要点: 本文原文地址:Power BI官方视频(1) P ...
- Android自定义View 画弧形,文字,并增加动画效果
一个简单的Android自定义View的demo,画弧形,文字,开启一个多线程更新ui界面,在子线程更新ui是不允许的,但是View提供了方法,让我们来了解下吧. 1.封装一个抽象的View类 B ...