WCF技术剖析之十八:消息契约(Message Contract)和基于消息契约的序列化

在本篇文章中,我们将讨论WCF四大契约(服务契约、数据契约、消息契约和错误契约)之一的消息契约(Message Contract)。服务契约关注于对服务操作的描述,数据契约关注于对于数据结构和格式的描述,而消息契约关注的是类型成员与消息元素的匹配关系。

我们知道只有可序列化的对象才能通过服务调用在客户端和服务端之间进行传递。到目前为止,我们知道的可序列化类型有两种:一种是应用了System.SerializableAttribute特性或者实现了System.Runtime.Serialization.ISerializable接口的类型;另一种是数据契约对象。对于基于这两种类型的服务操作,客户端通过System.ServiceModel.Dispatcher.IClientMessageFormatter将输入参数格式化成请求消息,输入参数全部内容作为有效负载置于消息的主体中;同样地,服务操作的执行结果被System.ServiceModel.Dispatcher.IDispatchMessageFormatter序列化后作为回复消息的主体。

在一些情况下,具有这样的要求:当序列化一个对象并生成消息的时候,希望将部分数据成员作为SOAP的报头,部分作为消息的主体。比如说,我们有一个服务操作采用流的方式进行文件的上载,除了以流的方式传输以二进制表示的文件内容外,还需要传输一个额外的基于文件属性的信息,比如文件格式、文件大小等。一般的做法是将传输文件内容的流作为SOAP的主体,将其属性内容作为SOAP的报头进行传递。这样的功能,可以通过定义消息契约来实现。

一、 消息契约的定义

消息契约和数据契约一样,都是定义在数据(而不是功能)类型上。不过数据契约旨在定义数据的结构(将数据类型与XSD进行匹配),而消息契约则更多地关注于数据的成员具体在SOAP消息中的表示。消息契约通过以下3个特性进行定义:System.ServiceModel.MessageContractAttributeSystem.ServiceModel.MessageHeaderAttributeSystem.ServiceModel.MessageBodyMemberAttribute。MessageContractAttribute应用于类型上,MessageHeaderAttribute和MessageBodyMemberAttribute则应用于属性或者字段成员上,表明相应的数据成员是一个基于SOAP报头的成员还是SOAP主体的成员。先来简单介绍一下这3个特性:

1、MessageContractAttribute

通过在一个类或者结构(Struct)上应用MessageContractAttribute使之成为一个消息契约。从MessageContractAttribute的定义来看,MessageContractAttribute大体上具有以下两种类型的属性成员:

  • ProtectionLevel和HasProtectionLevel:表示保护级别,在服务契约中已经对保护级别作了简单的介绍,WCF中通过System.Net.Security.ProtectionLevel枚举定义消息的保护级别。一般有3种可选的保护级别:None、Sign和EncryptAndSign
  • IsWrapped、WrapperName、WrapperNamespace:IsWrapped表述的含义是是否为定义的主体成员(一个或者多个)添加一个额外的根节点。WrapperName和WrapperNamespace则表述该根节点的名称和命名空间。IsWrapped、WrapperName、WrapperNamespace的默认是分别为true、类型名称和http://tempuri.org/
   1: [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = false)]
   2: public sealed class MessageContractAttribute : Attribute
   3: {  
   4:     //其他成员
   5:     public bool         HasProtectionLevel { get; }
   6:     public ProtectionLevel    ProtectionLevel { get; set; }
   7:  
   8:     public bool     IsWrapped { get; set; }
   9:     public string     WrapperName { get; set; }
  10:     public string     WrapperNamespace { get; set; }
  11: }

下面的代码中将Customer类型通过应用MessageContractAttribute使之成为一个消息契约。ID和Name属性通过应用MessageHeaderAttribute定义成消息报头(Header)成员,而Address属性则通过MessageBodyMemberAttribute定义成消息主体(Body)成员。后面的XML体现的是Customer对象在SOAP消息中的表现形式。

   1: [MessageContract]
   2: public class Customer
   3: {
   4:     [MessageHeader(Name = "CustomerNo", Namespace = "http://www.artech.com/")]
   5:     public Guid ID
   6:     { get; set; }
   7:  
   8:     [MessageHeader(Name = "CustomerName", Namespace = "http://www.artech.com/")]
   9:     public string Name
  10:     { get; set; }
  11:  
  12:     [MessageBodyMember(Namespace = "http://www.artech.com/")]
  13:     public string Address
  14:     { get; set; }
  15: }
   1: <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
   2:     <s:Header>
   3:         <a:Action s:mustUnderstand="1">http://tempuri.org/IOrderManager/ProcessOrder</a:Action>
   4:         <h:CustomerName xmlns:h="http://www.artech.com/">Foo</h:CustomerName>
   5:         <h:CustomerNo xmlns:h="http://www.artech.com/">2f62405b-a472-4d1c-8c03-b888f9bd0df9</h:CustomerNo>
   6:     </s:Header>
   7:     <s:Body>
   8:         <Customer xmlns="http://tempuri.org/">
   9:             <Address xmlns="http://www.artech.com/">#328, Airport Rd, Industrial Park, Suzhou Jiangsu Province</Address>
  10:         </Customer>
  11:     </s:Body>
  12: </s:Envelope> 

如果我们将IsWrapped的属性设为false,那么套在Address节点外的Customer节点将会从SOAP消息中去除。

   1: [MessageContract(IsWrapped = false)]
   2: public class Customer
   3: {
   4:       //省略成员
   5: }
   1: <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
   2:     ......
   3:     <s:Body>
   4:         <Address xmlns="http://www.artech.com/">#328, Airport Rd, Industrial Park, Suzhou Jiangsu Province</Address>
   5:     </s:Body>
   6: </s:Envelope>

我们同样可以自定义这个主体封套(Wrapper)的命名和命名空间。下面我们就通过将MessageContractAttribute的WrapperName和WrapperNamespace属性设为Cust和http://www.artech.com/。

   1: [MessageContract(IsWrapped = true, WrapperName = "Cust", WrapperNamespace = "http://www.artech.com/")]
   2: public class Customer
   3: {
   4:     //省略成员
   5: }
   1: <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
   2:     ......
   3:     <s:Body>
   4:         <Cust xmlns="http://www.artech.com/">
   5:             <Address>#328, Airport Rd, Industrial Park, Suzhou Jiangsu Province</Address>
   6:         </Cust>
   7:     </s:Body>
   8: </s:Envelope>

2、MessageHeaderAttribute

MessageHeaderAttribute和MessageBodyMemberAttribute分别用于定义消息报头成员和消息主体成员,它们都有一个共同的基类:System.ServiceModel.MessageContractMemberAttribute。MessageContractMemberAttribute定义了以下属性成员:HasProtectionLevel、ProtectionLevel、Name和Namespace。

   1: public abstract class MessageContractMemberAttribute : Attribute
   2: {   
   3:     public bool             HasProtectionLevel { get; }
   4:     public ProtectionLevel     ProtectionLevel { get; set; }
   5:  
   6:     public string             Name { get; set; }
   7:     public string             Namespace { get; set; }
   8: }

通过在属性或者字段成员上应用MessageHeaderAttribute使之成为一个消息报头成员。MessageHeaderAttribute定义了以下3个属性,如果读者对SOAP规范有一定了解的读者,相信对它们不会陌生。

注:在《WCF技术剖析(卷1)》中的第六章有对SOAP 1.2的基本规范有一个大致的介绍,读者也可以直接访问W3C网站下载官方文档。

  • 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节点
   1: [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
   2: public class MessageHeaderAttribute : MessageContractMemberAttribute
   3: {
   4:     public string     Actor { get; set; }
   5:     public bool     MustUnderstand { get; set; }
   6:     public bool     Relay { get; set; }
   7: }

同样使用上面定义的Customer消息契约,现在我们相应地修改了ID属性上的MessageHeaderAtribute设置:MustUnderstand
= true, Relay=true, Actor=http://www.w3.org/
2003/05/soap-envelope/role/ultimateReceiver。实际上将相应的SOAP报头的目标SOAP节点定义成最终的消息接收者。由于http://www.w3.org/2003/05/soap-envelope/role/ultimateReceiver是SOAP 1.2的预定义属性,所以这个消息契约之后在基于SOAP 1.2的消息版本中有效。后面给出的为对应的SOAP消息。

   1: [MessageContract(IsWrapped =true, WrapperNamespace="http://www.artech.com/")]public class Customer
   2: {
   3:     //其他成员
   4:     [MessageHeader(Name="CustomerNo", Namespace = "http://www.artech.com/" ,MustUnderstand = true, Relay=true, Actor="http://www.w3.org/2003/05/soap-envelope/role/ultimateReceiver" )]
   5:     public Guid ID
   6:     { get; set; }
   7:     
   8: }
   1: <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
   2:     <s:Header>
   3:         ......
   4:         <h:CustomerNo s:role="http://www.w3.org/2003/05/soap-envelope/role/ultimateReceiver" s:mustUnderstand="1" s:relay="1" xmlns:h="http://www.artech.com/">5330c91a-7fd7-4bf5-ae3e-4ba9bfef3d4d</h:CustomerNo>
   5:     </s:Header>
   6: ......
   7: </s:Envelope>

http://www.w3.org/2003/05/soap-envelope/role/ultimateReceiver在SOAP1.1中对应的表示为:"http://schemas.xmlsoap.org/soap/actor/ultimateReceiver(具有不同的命名空间)。如果在SOAP
1.1下,ID成员对应的MessageHeaderAttribute应该做如下的改动。从对应的SOAP消息来看,在SOAP
1.2中的role属性变成了actor属性。

   1: [MessageContract(IsWrapped =true, WrapperNamespace="http://www.artech.com/")]public class Customer
   2: {
   3:     //其他成员
   4:     [MessageHeader(Name="CustomerNo", Namespace = "http://www.artech.com/" ,MustUnderstand = true, Relay=true, Actor="http://schemas.xmlsoap.org/soap/actor/ultimateReceiver" )]
   5:     public Guid ID
   6:     { get; set; }    
   7: }
   1: <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
   2:     <s:Header>
   3:         ......
   4:         <h:CustomerNo s:actor="http://schemas.xmlsoap.org/soap/actor/ultimateReceiver" s:mustUnderstand="1" xmlns:h="http://www.artech.com/">e48a8897-c644-49f8-b5e7-cd16be4c75b7</h:CustomerNo>
   5:     </s:Header>
   6:     ......
   7: </s:Envelope>

3、MessageBodyMemberAttribute

MessageBodyMemberAttribute应用于属性或者字段成员,应用了该特性的属性或者字段的内容将会出现在SOAP的主体部分。MessageBodyMemberAttribute的定义显得尤为简单,仅仅具有一个Order对象,用于控制成员在SOAP消息主体中出现的位置。默认的排序规则是基于字母排序。

可能细心的读者会问,为什么MessageHeaderAttribute中没有这样Order属性呢?原因很简单,MessageHeaderAttribute定义的是单个SOAP报头,SOAP消息报头集合中的每个报头元素是次序无关的。而MessageBodyMemberAttribute则是定义SOAP主体的某个元素,主体成员之间的次序也是契约的一个重要组成部分。所以MessageHeaderAttribute不叫MessageHeaderMemberAttribute。

   1: [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false)]
   2: public class MessageBodyMemberAttribute : MessageContractMemberAttribute
   3: {
   4:     public int Order { get; set; }
   5: }

二、实例演示:基于消息契约的方法调用是如何格式化成消息的?

在WCF体系中,MessageFormatter负责序列化和反序列化任务(在《WCF技术剖析(卷1)》中的第5章对基于MessageFormatter的序列化机制有详细的介绍):ClientMessageFormatter和DispatchMessageFormatter分别在客户端和服务端,根据操作的描述(Operation

Description),借助于相应的序列化器(Serializer)实现了方法调用与消息之间的转换。接下来,我将通过一个实实在在的案例程序为大家演示如何通过ClientMessageFormatter将输入参数转换为基于当前服务操作的Message。由于本节的主题是消息契约,所以在这里我们将转换对象限定为消息契约。不过,不论是消息参数还是一般的可序列化对象,其转换过程都是一样的。

步骤一:创建消息契约

本案例模拟一个订单处理的WCF应用,我们首先定义如下一个Order类型。Order是一个消息契约,属性OrderID和Date通过MessageHeaderAttribute定义成消息报头,作为主体的Details的类型OrderDetails被定义成集合数据契约。OrderDetails的元素类型是数据契约OrderDetail,代表订单中每笔产品明细。

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Runtime.Serialization;
   4: using System.ServiceModel;
   5: namespace Artech.TypedMessage
   6: {
   7:     [MessageContract]
   8:     public class Order
   9:     {
  10:         [MessageHeader(Namespace ="http://www.artech.com/")]
  11:         public Guid OrderID
  12:         { get; set; }
  13:  
  14:         [MessageHeader(Namespace ="http://www.artech.com/")]
  15:         public DateTime Date
  16:         { get; set; }
  17:  
  18:         [MessageBodyMember]
  19:         public OrderDetails Details
  20:         { get; set; }
  21:  
  22:       public override string ToString()
  23:         {
  24:             return string.Format("Oder ID: {0}\nDate: {1}\nDetail Count: {2}",this.OrderID,this.Date.ToShortDateString(),this.Details.Count);
  25:         }
  26:     }
  27:  
  28:     [CollectionDataContract(ItemName = "Detail",Namespace ="http://www.artech.com/")]
  29:     public class OrderDetails : List<OrderDetail>
  30:     { }
  31:  
  32:     [DataContract(Namespace ="http://www.artech.com/")]
  33:     public class OrderDetail
  34:     {
  35:         [DataMember]
  36:         public Guid ProductID
  37:         { get; set; }
  38:  
  39:         [DataMember]
  40:         public int Quantity
  41:         { get; set; }
  42:     }
  43: }

步骤二:创建MessageFormatter

本例的目的在于重现WCF如何通过ClientMessageFormatter实现将输入参数序列化成请求消息,以及通过DispatchMessageFormatter实现将请求消息反序列化成输入参数。根据使用的序列化器的不同,WCF中定义了两种典型的MessageFormatter:一种是基于DataContractSerializer的DataContractSerializerOperationFormatter;另一种则是基于XmlSerializer的XmlSerializerOperationFormatter。由于DataContractSerializerOperationFormatter是默认的MessageFormatter,所以我们这个案例就采用DataContractSerializerOperationFormatter。

我们的任务就是创建这个DataContractSerializerOperationFormatter。由于这是一个定义在System.ServiceModel.Dispatcher命名空间下的内部(internal)类型,所以我们只能通过反射的机制调用构造函数来创建这个对象。DataContractSerializerOperationFormatter定义了唯一的一个构造函数,3个输入参数类型分别为:OperationDescription,DataContractFormatAttribute和DataContractSerializerOperationBehavior。

   1: internal class DataContractSerializerOperationFormatter : OperationFormatter
   2: {    
   3:     //其他成员
   4:     public DataContractSerializerOperationFormatter(OperationDescription description, DataContractFormatAttribute dataContractFormatAttribute, DataContractSerializerOperationBehavior serializerFactory);
   5: }

为此我们定义下面一个辅助方法CreateMessageFormatter<TFormatter,
TContract>。TFormatter代表MessageFormatter的两个接口:IClientMessageFormatter和IDispatchMessageFormatter(DataContractSerializerOperationFormatter同时实现了这两个接口),TContract则是服务契约的类型。参数operationName为当前操作的名称。代码不算复杂,主要的流程如下:通过服务契约类型创建ContractDescription,根据操作名称得到OperationDescription对象。通过反射机制调用DataContractSerializerOperationFormatter的构造函数创建该对象。

   1: static TFormatter CreateMessageFormatter<TFormatter, TContract>(string operationName)
   2: {
   3:     ContractDescription contractDesc = ContractDescription.GetContract(typeof(TContract));
   4:     var operationDescs = contractDesc.Operations.Where(op => op.Name == operationName);
   5:     if(operationDescs.Count() == 0)
   6:     {
   7:        throw new ArgumentException("operationName","Invalid operation name.");
   8:     }
   9:     OperationDescription operationDesc = operationDescs.ToArray()[0];
  10:     string formatterTypeName = "System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter,System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
  11:     Type formatterType = Type.GetType(formatterTypeName);
  12:     ConstructorInfo constructor = formatterType.GetConstructor(new Type[] { typeof(OperationDescription), typeof(DataContractFormatAttribute), typeof(DataContractSerializerOperationBehavior) });
  13: return (TFormatter)constructor.Invoke(new object[] { operationDesc, new DataContractFormatAttribute(), null });
  14: }   

MessageFormatter已经创建出来了,序列化与反序列化的问题就很简单了。为此我定义了以下两个辅助方法:SerializeRequest<TContract>和DeserializeRequest<TContract>,具体实现就是调用创建出来的MessageFormatter的同名方法。

   1: static Message SerializeRequest<TContract>(MessageVersion messageVersion, string operationName, params object[] values)
   2: {
   3:     IClientMessageFormatter formatter = CreateMessageFormatter<IClientMessageFormatter, TContract>(operationName);
   4:     return formatter.SerializeRequest(messageVersion, values);
   5: } 
   6:  
   7: static void DeserializeRequest<TContract>(Message message, string operationName, object[] parameters)
   8: {
   9:     IDispatchMessageFormatter formatter = CreateMessageFormatter<IDispatchMessageFormatter, TContract>(operationName);
  10:     formatter.DeserializeRequest(message, parameters);
  11: }

步骤三:通过MessageFormmatter实现消息的格式化

现在我们通过一个简单的例子来演示通过上面创建的MessageFormatter实现对消息的格式化。由于MessageFormatter进行序列化和反序列化依赖于操作的描述(消息的结构本来就是由操作决定的),为此我们定义了一个服务契约IOrderManager。操作ProcessOrder将消息契约Order作为唯一的参数。

   1: using System.ServiceModel;
   2: namespace Artech.TypedMessage
   3: {
   4:     [ServiceContract]
   5:     public interface IOrderManager
   6:     {
   7:         [OperationContract]
   8:         void ProcessOrder(Order order);
   9:     }
  10: }

在下面的代码中,先调用SerializeRequest<IOrderManager>方法将Order对象进行序列化并生成Message对象,该过程实际上体现了WCF的客户端框架是如何通过ClientMessageFormatter将操作方法调用连同输入参数转换成请求消息的。随后,调用DeserializeRequest<IOrderManager>方法将Message对象反序列化成Order对象,该过程则代表WCF的服务端框架是如何通过DispatchMessageFormatter将请求消息反序列化成输入参数的。

   1: OrderDetail detail1 = new OrderDetail
   2: {
   3:     ProductID = Guid.NewGuid(),
   4:     Quantity = 666
   5: }; 
   6:  
   7: OrderDetail detail2 = new OrderDetail
   8: {
   9:     ProductID = Guid.NewGuid(),
  10:     Quantity = 999
  11: }; 
  12:  
  13: Order order = new Order
  14: {
  15:     OrderID = Guid.NewGuid(),
  16:     Date = DateTime.Today,
  17:     Details = new OrderDetails { detail1, detail2 }
  18: }; 
  19: //模拟WCF客户端的序列化
  20: Message message = SerializeRequest<IOrderManager>(MessageVersion.Default, "ProcessOrder", order);
  21: MessageBuffer buffer = message.CreateBufferedCopy(int.MaxValue);
  22: WriteMessage(buffer.CreateMessage(), "message.xml"); 
  23:  
  24: //模拟WCF服务端的反序列化
  25: object[] DeserializedOrder = new object[]{ null };
  26: DeserializeRequest<IOrderManager>(buffer.CreateMessage(), "ProcessOrder", DeserializedOrder);
  27: Console.WriteLine(DeserializedOrder[0]);

下面的XML表示调用SerializeRequest<IOrderManager>生成的SOAP消息。程序最终的输出结果也表明了反序列化的成功执行。

   1: <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
   2:     <s:Header>
   3:         <a:Action s:mustUnderstand="1">http://tempuri.org/IOrderManager/ProcessOrder</a:Action>
   4:         <h:Date xmlns:h="http://www.artech.com/">2008-12-21T00:00:00+08:00</h:Date>
   5:         <h:OrderID xmlns:h="http://www.artech.com/">cd94a6f0-7e21-4ace-83f7-2ddf061cfbbe</h:OrderID>
   6:     </s:Header>
   7:     <s:Body>
   8:         <Order xmlns="http://tempuri.org/">
   9:             <Details xmlns:d4p1="http://www.artech.com/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  10:                 <d4p1:Detail>
  11:                     <d4p1:ProductID>bc2a186d-569a-4146-9b97-3693248104c0</d4p1:ProductID>
  12:                     <d4p1:Quantity>666</d4p1:Quantity>
  13:                 </d4p1:Detail>
  14:                 <d4p1:Detail>
  15:                     <d4p1:ProductID>72687c23-c2b2-4451-b6c3-da6d040587fc</d4p1:ProductID>
  16:                     <d4p1:Quantity>999</d4p1:Quantity>
  17:                 </d4p1:Detail>
  18:             </Details>
  19:         </Order>
  20:     </s:Body>
  21: </s:Envelope>
   1: Oder ID: cd94a6f0-7e21-4ace-83f7-2ddf061cfbbe
   2: Date: 12/21/2008
   3: Detail Count: 2

作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文链接

时间: 2024-10-31 23:07:54

WCF技术剖析之十八:消息契约(Message Contract)和基于消息契约的序列化的相关文章

WCF技术剖析之十八:消息契约(Message Contract)和基于消息契约的序列化

在本篇文章中,我们将讨论WCF四大契约(服务契约.数据契约.消息契约和错误契约)之一的消息契约(Message Contract).服务契约关注于对服务操作的描述,数据契约关注于对于数据结构和格式的描述,而消息契约关注的是类型成员与消息元素的匹配关系. 我们知道只有可序列化的对象才能通过服务调用在客户端和服务端之间进行传递.到目前为止,我们知道的可序列化类型有两种:一种是应用了System.SerializableAttribute特性或者实现了System.Runtime.Serializat

WCF技术剖析之十二:数据契约(Data Contract)和数据契约序列化器(DataContractSerializer)

原文:WCF技术剖析之十二:数据契约(Data Contract)和数据契约序列化器(DataContractSerializer) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话)]]大部分的系统都是以数据为中心的(Data Central),功能的实现表现在对相关数据的正确处理.而数据本身,是有效信息的载体,在不同的环境具有不同的表示.一个分布式的互联系统关注于数据的交换,而数据正常交换的根本前提是参与数据交换的双方对于数据结

WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用

原文:WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用 [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话)]]如果一个类型,不一定是数据契约,和给定的数据契约具有很大的差异,而我们要将该类型的对象序列化成基于数据契约对应的XML.反之,对于一段给定的基于数据契约的XML,要通过反序列化生成该类型的对象,我们该如何实现这样的场景? 比如下面定义了两个类型Contact和Customer,

WCF技术剖析之三十二:一步步创建一个完整的分布式事务应用

在完成了对于WCF事务编程(<上篇>.<中篇>.<下篇>)的介绍后,本篇文章将提供一个完整的分布式事务的WCF服务应用,通过本例,读者不仅仅会了解到如何编程实现事务型服务,还会获得其他相关的知识,比如DTC和AS-AT的配置等.本例还是沿用贯通本章的应用场景:银行转帐.我们将会创建一个BankingService服务,并将其中的转帐操作定义成事务型操作.我们先从物理部署的角度来了解一下BankingService服务,以及需要实现怎样的分布式事务. 一.从部署的角度看分

WCF技术剖析之十二

数据契约(Data Contract)和数据契约序列化器(DataContractSerializer) 大部分的系统都是以数据为中心的(Data Central),功能的实现表现在对相关数据的正确处理.而数据本身,是有效信息的载体,在不同的环境具有不同的表示.一个分布式的互联系统关注于数据的交换,而数据正常交换的根本前提是参与数据交换的双方对于数据结构的一致性理解.这就为数据的表现提出了要求,为了保证处于不同平台.不同厂商的应用能够正常地进行数据交换,交换的数据必须采用一种大家都能够理解的展现

WCF技术剖析之三十:一个很有用的WCF调用编程技巧[下篇]

在<上篇>中,我通过使用Delegate的方式解决了服务调用过程中的异常处理以及对服务代理的关闭.对于<WCF技术剖析(卷1)>的读者,应该会知道在第7章中我通过类似于AOP的方式解决了相似的问题,现在我们来讨论这个解决方案. 通过<服务代理不能得到及时关闭会有什么后果?>的介绍,我们知道了及时关闭服务代理的重要意义,并且给出了正确的编程方式.如果严格按照上面的编程方式,就意味着对于每一个服务调用,都要使用相同的代码进行异常处理和关闭或中断服务代理对象.按照我个人的观点

WCF技术剖析之十六:数据契约的等效性和版本控制

数据契约是对用于交换的数据结构的描述,是数据序列化和反序列化的依据.在一个WCF应用中,客户端和服务端必须通过等效的数据契约方能进行有效的数据交换.随着时间的推移,不可避免地,我们会面临着数据契约版本的变化,比如数据成员的添加和删除.成员名称或者命名空间的修正等,如何避免数据契约这种版本的变化对客户端现有程序造成影响,就是本节着重要讨论的问题. 一.数据契约的等效性 数据契约就是采用一种厂商中立.平台无关的形式(XSD)定义了数据的结构,而WCF通过DataContractAttribute和D

WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用

如果一个类型,不一定是数据契约,和给定的数据契约具有很大的差异,而我们要将该类型的对象序列化成基于数据契约对应的XML.反之,对于一段给定的基于数据契约的XML,要通过反序列化生成该类型的对象,我们该如何实现这样的场景? 比如下面定义了两个类型Contact和Customer,其中Customer是数据契约,Contact的Sex属性相当于Customer的Gender属性,而Contact的FullName可以看成是Customer的FirstName和LastName的组合.现在我们要做的是

WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇)

在.NET中,所有的集合都实现了IEnumerable接口,比如Array.Hashtable.ArrayList.Stack.Queue等.有的集合要求元素具有相同的类型,这种集合一般通过泛型的方式定义,它们实现另一个接口IEnumerable<T>(IEnumerable<T>本身继承自IEnumerable),这样的集合有List<T>.Dictionary<TKey,TValue>.Stack<T>.Queue<T>等.基于集