Contract
契约,用于提供消息的标准,消息交换的规则。它分四类:
·服务契约 定义操作
·数据契约 定义数据
·异常契约 定义异常
·消息契约 定义消息格式
(一)服务契约
服务契约,可以用接口定义,也可以直接在类上定义。
例如:
[ServiceContract]
public interface ICaculateService
它有很多可选属性,例如:
Name,Namespace,CallbackContract,SessionMode等等。这里来从包中来演示一下:
契约:
[ServiceContract]
public interface ICaculateService
{
[OperationContract]
int Devide(int a, int b);
}
上边的契约没有显示使用其它的属性
现在的包的情况:
<a:Action s:mustUnderstand="1"
u:Id="_2">http://tempuri.org/ICaculateService/DevideResponse
</a:Action>
在应用namespace之后:
[ServiceContract(Namespace="http://www.selftest.com/test1")]
public interface ICaculateService
情况为:
<a:Action s:mustUnderstand="1" u:Id="_2">
http://www.selftest.com/test1/ICaculateService/DevideResponse
</a:Action>
这个namespace的定义为:获取或设置 Web 服务描述语言 (WSDL) 中的 <portType> 元素的命名空间,它默认的为 http://tempuri.org
OperationContractAttribute用于标注向远程调用公开的方法功能。只要公开了,即使访问性为私有,也可以被远程调用。
(接口中定义默认公开的,也无法添加访问修饰符),这个标签也有很多可选属性,而且这些属性非常重要。例如:
Action,IsInitiating,IsOneWay,IsTerminating,Name
例如Action,
引用:
使用 Action 属性控制方法的输入消息的操作。由于 WCF 使用该操作将传入消息调度至相应方法,因此在协定操作中使用的消息必须具有唯一的操作。默认操作值由以下几项组成:协定命名空间(默认值为“http://tempuri.org/”)、协定名称(如果没有使用显式服务接口,则为接口名称或类名)、操作名称,并且如果该消息是一个相关的响应,则还有一个附加字符串(“Response”)。您可以使用 Action 属性重写该默认值:
<a:Action s:mustUnderstand="1"
u:Id="_2">http://tempuri.org/ICaculateService/DevideResponse
</a:Action>
(二)数据契约
DataContract
对于系统的简单类型,已经有默认的规则(所有 .NET Framework 基元类型(如整型和字符串型)以及某些被视为基元的类型(如 DateTime 和 XmlElement)无需做其他任何准备工作就可序列化并被视为拥有默认数据协定)。而对于复杂类型来说,如类对象,如果不进行处理,是不可以传输的。而如果要让复杂类型传输或存储,则要对其进行序列化。(若要使其类型可序列化,类型作者必须为其类型定义数据协定。)
DataContract属性可用于类,结构,枚举,然后其中的所有成员都必须添加DataMember标签,来指示这些成员做为数据成员,而可以被序列化和反序列化
可以被序列化的类型包括:
·数据契约类型,就是添加DataContract标签的类型
·集合,数组与集合
·枚举,可以对枚举类型添加DataContract标签,然后,必须对枚举成员添加EnumMember标签,例如:
[DataContract]
public enum eErrorType
{
[EnumMember]
No1,
[EnumMember]
No2,
[EnumMember]
No3,
[EnumMember]
No4
}
·.NET Framework 基元类型 包括,字符,字串,字节,数值类型,object,时间,时间戳,guid,Uri等
·用Serializable修饰的类型
·原始的xml和ado.net关系数据的类型
(三)异常契约
用于在服务操作在遇到处理错误时,返回的soap错误。这个通过FaultContractAttribute标签来实现。
WCF服务在两种错误系统下工作(一是托管的应用程序中的Exception异常,另一种是soap异常),所以,发送到客户端的异常要由托管的异常信息转化为Soap异常信息。
这里说一下FaultException<T> 泛型类,其中T表示可序列化错误详细信息类型。
例如:
·契约
[FaultContract(typeof(eErrorType))]
int Devide(int a, int b);
·服务
public int Devide(int a, int b)
{
switch (b)
{
case 0:
throw new FaultException<eErrorType>(eErrorType.No1);
case 1:
throw new FaultException<eErrorType>(eErrorType.No2);
}
return a /b;
}
其中eErrorType前文已经给出
客户端:
int result = client.Devide(10, 0);
catch (FaultException<FirstInstance.eErrorType> ex)
{
Console.WriteLine(ex.Detail.ToString());
}
异常信息:No1
(四)消息契约
WCF的核心是消息交换。例如WSHttpBinding绑定,编码格式为文本的时候,那么消息就是一个完整的Soap,就是一个封套;如果是MTOM时,那就是另一种格式了。
为了验证消息的格式,现在在绑定中把安全选项加上,使用不加密:
<wsHttpBinding>
<binding name="BindingSelf"
messageEncoding="Text" textEncoding="utf-8">
<security mode="None"></security>
</binding>
</wsHttpBinding>
然后,看一看消息格式是怎么样的:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">
http://www.selftest.com/test1/
ICaculateService/GetCustomerByIdResponse
</a:Action>
<a:RelatesTo>
urn:uuid:944ad5e1-7c2a-4617-a7a1-64bdc5b25d47
</a:RelatesTo>
</s:Header>
<s:Body>
<GetCustomerByIdResponse
xmlns="http://www.selftest.com/test1">
<GetCustomerByIdResult
xmlns:b=" http://schemas.datacontract.org
/2004/07/FirstService "
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<b:Address>Shandong</b:Address>
<b:CustomerName>Songjiang</b:CustomerName>
<b:Unid>1</b:Unid>
</GetCustomerByIdResult>
</GetCustomerByIdResponse>
</s:Body>
</s:Envelope>
在服务端是这样契约的:
[DataContract]
public class Customer
{
[DataMember]
public int Unid { get; set; }
[DataMember]
public string CustomerName { get; set; }
[DataMember]
public string Address { get; set; }
}
方法GetCustomerById返回了一个Customer对象:
<b:Address>Shandong</b:Address>
<b:CustomerName>Songjiang</b:CustomerName>
<b:Unid>1</b:Unid>
这些就是它的属性及值。
现在通过消息契约,来设置一下消息的格式。
[MessageContract]
public class Customer
{
[MessageHeader(Name="selfHeader001")]
public int Unid { get; set; }
[MessageHeader(Name = "selfHeader002")]
public string CustomerName { get; set; }
[MessageBodyMember(Name="selfBody001")]
public string Address { get; set; }
}
<s:Envelope
xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">
http://www.selftest.com/test1/ICaculateService
/GetCustomerByIdResponse
</a:Action>
<h:selfHeader001
xmlns:h="http://www.selftest.com/test1">
1
</h:selfHeader001>
<h:selfHeader002
xmlns:h="http://www.selftest.com/test1">
Songjiang
</h:selfHeader002>
<a:RelatesTo>
urn:uuid:6ea8a734-d01f-4cf4-85ce-5fae03e538d9
</a:RelatesTo>
</s:Header>
<s:Body>
<Customer xmlns="http://www.selftest.com/test1">
<selfBody001>Shandong</selfBody001>
</Customer>
</s:Body>
</s:Envelope>
可以看出这种消息契约非常灵活。现在来为customer消息添加一个文本文件。
[MessageBodyMember(Name="text001")]
public Byte[] TextFile { get; set; }
包中的结果
<text001>MTIzNCBxd2VydA==</text001>