日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發(fā)布時間:2023/12/18 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 WCF技术剖析之十八:消息契约(Message Contract)和基于消息契约的序列化 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

在本篇文章中,我們將討論WCF四大契約(服務(wù)契約、數(shù)據(jù)契約、消息契約和錯誤契約)之一的消息契約(Message Contract)。服務(wù)契約關(guān)注于對服務(wù)操作的描述,數(shù)據(jù)契約關(guān)注于對于數(shù)據(jù)結(jié)構(gòu)和格式的描述,而消息契約關(guān)注的是類型成員與消息元素的匹配關(guān)系。

我們知道只有可序列化的對象才能通過服務(wù)調(diào)用在客戶端和服務(wù)端之間進行傳遞。到目前為止,我們知道的可序列化類型有兩種:一種是應(yīng)用了System.SerializableAttribute特性或者實現(xiàn)了System.Runtime.Serialization.ISerializable接口的類型;另一種是數(shù)據(jù)契約對象。對于基于這兩種類型的服務(wù)操作,客戶端通過System.ServiceModel.Dispatcher.IClientMessageFormatter將輸入?yún)?shù)格式化成請求消息,輸入?yún)?shù)全部內(nèi)容作為有效負載置于消息的主體中;同樣地,服務(wù)操作的執(zhí)行結(jié)果被System.ServiceModel.Dispatcher.IDispatchMessageFormatter序列化后作為回復(fù)消息的主體。

在一些情況下,具有這樣的要求:當(dāng)序列化一個對象并生成消息的時候,希望將部分數(shù)據(jù)成員作為SOAP的報頭,部分作為消息的主體。比如說,我們有一個服務(wù)操作采用流的方式進行文件的上載,除了以流的方式傳輸以二進制表示的文件內(nèi)容外,還需要傳輸一個額外的基于文件屬性的信息,比如文件格式、文件大小等。一般的做法是將傳輸文件內(nèi)容的流作為SOAP的主體,將其屬性內(nèi)容作為SOAP的報頭進行傳遞。這樣的功能,可以通過定義消息契約來實現(xiàn)。

一、 消息契約的定義

消息契約和數(shù)據(jù)契約一樣,都是定義在數(shù)據(jù)(而不是功能)類型上。不過數(shù)據(jù)契約旨在定義數(shù)據(jù)的結(jié)構(gòu)(將數(shù)據(jù)類型與XSD進行匹配),而消息契約則更多地關(guān)注于數(shù)據(jù)的成員具體在SOAP消息中的表示。消息契約通過以下3個特性進行定義:System.ServiceModel.MessageContractAttributeSystem.ServiceModel.MessageHeaderAttributeSystem.ServiceModel.MessageBodyMemberAttributeMessageContractAttribute應(yīng)用于類型上,MessageHeaderAttributeMessageBodyMemberAttribute則應(yīng)用于屬性或者字段成員上,表明相應(yīng)的數(shù)據(jù)成員是一個基于SOAP報頭的成員還是SOAP主體的成員。先來簡單介紹一下這3個特性:

1MessageContractAttribute

通過在一個類或者結(jié)構(gòu)(Struct)上應(yīng)用MessageContractAttribute使之成為一個消息契約。從MessageContractAttribute的定義來看,MessageContractAttribute大體上具有以下兩種類型的屬性成員:

  • ProtectionLevelHasProtectionLevel:表示保護級別,在服務(wù)契約中已經(jīng)對保護級別作了簡單的介紹,WCF中通過System.Net.Security.ProtectionLevel枚舉定義消息的保護級別。一般有3種可選的保護級別:NoneSignEncryptAndSign
  • IsWrappedWrapperNameWrapperNamespaceIsWrapped表述的含義是是否為定義的主體成員(一個或者多個)添加一個額外的根節(jié)點。WrapperNameWrapperNamespace則表述該根節(jié)點的名稱和命名空間。IsWrappedWrapperNameWrapperNamespace的默認是分別為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類型通過應(yīng)用MessageContractAttribute使之成為一個消息契約。IDName屬性通過應(yīng)用MessageHeaderAttribute定義成消息報頭(Header)成員,而Address屬性則通過MessageBodyMemberAttribute定義成消息主體(Body)成員。后面的XML體現(xiàn)的是Customer對象在SOAP消息中的表現(xiàn)形式。

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的屬性設(shè)為false,那么套在Address節(jié)點外的Customer節(jié)點將會從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)的命名和命名空間。下面我們就通過將MessageContractAttributeWrapperNameWrapperNamespace屬性設(shè)為Custhttp://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>

2MessageHeaderAttribute

MessageHeaderAttributeMessageBodyMemberAttribute分別用于定義消息報頭成員和消息主體成員,它們都有一個共同的基類:System.ServiceModel.MessageContractMemberAttributeMessageContractMemberAttribute定義了以下屬性成員:HasProtectionLevelProtectionLevelNameNamespace

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: }

通過在屬性或者字段成員上應(yīng)用MessageHeaderAttribute使之成為一個消息報頭成員。MessageHeaderAttribute定義了以下3個屬性,如果讀者對SOAP規(guī)范有一定了解的讀者,相信對它們不會陌生。

注:在《WCF技術(shù)剖析(卷1)》中的第六章有對SOAP 1.2的基本規(guī)范有一個大致的介紹,讀者也可以直接訪問W3C網(wǎng)站下載官方文檔。

  • Actor:表示處理該報頭的目標(biāo)節(jié)點(SOAP Node),SOAP1.1中對應(yīng)的屬性(Attribute)為actorSOAP 1.2中就是我們介紹的role屬性
  • MustUnderstand:表述ActorSOAP 1.1)或者RoleSOAP 1.2)定義的SOAP節(jié)點是否必須理解并處理該節(jié)點。對應(yīng)的SOAP報頭屬性為mustUnderstand
  • Relay:對應(yīng)的SOAP報頭屬性為relay,表明該報頭是否需要傳遞到下一個SOAP節(jié)點

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消息契約,現(xiàn)在我們相應(yīng)地修改了ID屬性上的MessageHeaderAtribute設(shè)置:MustUnderstand = true, Relay=true, Actor=http://www.w3.org/ 2003/05/soap-envelope/role/ultimateReceiver。實際上將相應(yīng)的SOAP報頭的目標(biāo)SOAP節(jié)點定義成最終的消息接收者。由于http://www.w3.org/2003/05/soap-envelope/role/ultimateReceiverSOAP 1.2的預(yù)定義屬性,所以這個消息契約之后在基于SOAP 1.2的消息版本中有效。后面給出的為對應(yīng)的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/ultimateReceiverSOAP1.1中對應(yīng)的表示為:"http://schemas.xmlsoap.org/soap/actor/ultimateReceiver(具有不同的命名空間)。如果在SOAP 1.1下,ID成員對應(yīng)的MessageHeaderAttribute應(yīng)該做如下的改動。從對應(yīng)的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>

3MessageBodyMemberAttribute

MessageBodyMemberAttribute應(yīng)用于屬性或者字段成員,應(yīng)用了該特性的屬性或者字段的內(nèi)容將會出現(xiàn)在SOAP的主體部分。MessageBodyMemberAttribute的定義顯得尤為簡單,僅僅具有一個Order對象,用于控制成員在SOAP消息主體中出現(xiàn)的位置。默認的排序規(guī)則是基于字母排序。

可能細心的讀者會問,為什么MessageHeaderAttribute中沒有這樣Order屬性呢?原因很簡單,MessageHeaderAttribute定義的是單個SOAP報頭,SOAP消息報頭集合中的每個報頭元素是次序無關(guān)的。而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: }

二、實例演示:基于消息契約的方法調(diào)用是如何格式化成消息的?

WCF體系中,MessageFormatter負責(zé)序列化和反序列化任務(wù)(在《WCF技術(shù)剖析(卷1)》中的第5章對基于MessageFormatter的序列化機制有詳細的介紹):ClientMessageFormatterDispatchMessageFormatter分別在客戶端和服務(wù)端,根據(jù)操作的描述(Operation Description),借助于相應(yīng)的序列化器(Serializer)實現(xiàn)了方法調(diào)用與消息之間的轉(zhuǎn)換。接下來,我將通過一個實實在在的案例程序為大家演示如何通過ClientMessageFormatter將輸入?yún)?shù)轉(zhuǎn)換為基于當(dāng)前服務(wù)操作的Message。由于本節(jié)的主題是消息契約,所以在這里我們將轉(zhuǎn)換對象限定為消息契約。不過,不論是消息參數(shù)還是一般的可序列化對象,其轉(zhuǎn)換過程都是一樣的。

步驟一:創(chuàng)建消息契約

本案例模擬一個訂單處理的WCF應(yīng)用,我們首先定義如下一個Order類型。Order是一個消息契約,屬性OrderIDDate通過MessageHeaderAttribute定義成消息報頭,作為主體的Details的類型OrderDetails被定義成集合數(shù)據(jù)契約。OrderDetails的元素類型是數(shù)據(jù)契約OrderDetail,代表訂單中每筆產(chǎn)品明細。

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: }

步驟二:創(chuàng)建MessageFormatter

本例的目的在于重現(xiàn)WCF如何通過ClientMessageFormatter實現(xiàn)將輸入?yún)?shù)序列化成請求消息,以及通過DispatchMessageFormatter實現(xiàn)將請求消息反序列化成輸入?yún)?shù)。根據(jù)使用的序列化器的不同,WCF中定義了兩種典型的MessageFormatter:一種是基于DataContractSerializerDataContractSerializerOperationFormatter;另一種則是基于XmlSerializerXmlSerializerOperationFormatter。由于DataContractSerializerOperationFormatter是默認的MessageFormatter,所以我們這個案例就采用DataContractSerializerOperationFormatter

我們的任務(wù)就是創(chuàng)建這個DataContractSerializerOperationFormatter。由于這是一個定義在System.ServiceModel.Dispatcher命名空間下的內(nèi)部(internal)類型,所以我們只能通過反射的機制調(diào)用構(gòu)造函數(shù)來創(chuàng)建這個對象。DataContractSerializerOperationFormatter定義了唯一的一個構(gòu)造函數(shù),3個輸入?yún)?shù)類型分別為:OperationDescriptionDataContractFormatAttributeDataContractSerializerOperationBehavior

1: internal class DataContractSerializerOperationFormatter : OperationFormatter

2: {

3: //其他成員

4: public DataContractSerializerOperationFormatter(OperationDescription description, DataContractFormatAttribute dataContractFormatAttribute, DataContractSerializerOperationBehavior serializerFactory);

5: }

為此我們定義下面一個輔助方法CreateMessageFormatter<TFormatter, TContract>TFormatter代表MessageFormatter的兩個接口:IClientMessageFormatterIDispatchMessageFormatterDataContractSerializerOperationFormatter同時實現(xiàn)了這兩個接口),TContract則是服務(wù)契約的類型。參數(shù)operationName為當(dāng)前操作的名稱。代碼不算復(fù)雜,主要的流程如下:通過服務(wù)契約類型創(chuàng)建ContractDescription,根據(jù)操作名稱得到OperationDescription對象。通過反射機制調(diào)用DataContractSerializerOperationFormatter的構(gòu)造函數(shù)創(chuàng)建該對象。

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已經(jīng)創(chuàng)建出來了,序列化與反序列化的問題就很簡單了。為此我定義了以下兩個輔助方法:SerializeRequest<TContract>DeserializeRequest<TContract>,具體實現(xiàn)就是調(diào)用創(chuàng)建出來的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實現(xiàn)消息的格式化

現(xiàn)在我們通過一個簡單的例子來演示通過上面創(chuàng)建的MessageFormatter實現(xiàn)對消息的格式化。由于MessageFormatter進行序列化和反序列化依賴于操作的描述(消息的結(jié)構(gòu)本來就是由操作決定的),為此我們定義了一個服務(wù)契約IOrderManager。操作ProcessOrder將消息契約Order作為唯一的參數(shù)。

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: }

在下面的代碼中,先調(diào)用SerializeRequest<IOrderManager>方法將Order對象進行序列化并生成Message對象,該過程實際上體現(xiàn)了WCF的客戶端框架是如何通過ClientMessageFormatter將操作方法調(diào)用連同輸入?yún)?shù)轉(zhuǎn)換成請求消息的。隨后,調(diào)用DeserializeRequest<IOrderManager>方法將Message對象反序列化成Order對象,該過程則代表WCF的服務(wù)端框架是如何通過DispatchMessageFormatter將請求消息反序列化成輸入?yún)?shù)的。

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服務(wù)端的反序列化

25: object[] DeserializedOrder = new object[]{ null };

26: DeserializeRequest<IOrderManager>(buffer.CreateMessage(), "ProcessOrder", DeserializedOrder);

27: Console.WriteLine(DeserializedOrder[0]);

下面的XML表示調(diào)用SerializeRequest<IOrderManager>生成的SOAP消息。程序最終的輸出結(jié)果也表明了反序列化的成功執(zhí)行。

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

轉(zhuǎn)載于:https://www.cnblogs.com/qq260250932/p/5350905.html

總結(jié)

以上是生活随笔為你收集整理的WCF技术剖析之十八:消息契约(Message Contract)和基于消息契约的序列化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。