第七讲:数据契约(2)
代碼
https://yunpan.cn/cPns5DkGnRGNs ? 密碼:3913
?
前面不止一次地強調,WCF下的序列化與反序列化解決的是數據在兩種狀態之間 相互 轉化的問題:托管類型對象轉換成XML 。
由于類型定義了對象的數據結構,所以無論是對于序列化還是反序列化,都必須事先確定對象的類型。如果被序列化對象或被反序列化生成對象包含不可知的類型,序列化和反序列化將會失敗。為了確保DataContractSerializer的正常序列化和飯序列化,需要將“未知”類型加入DataContractSerializer“已知”類型列表中。
?
例如 :
我們服務端的數據類型 A ?和 ?B(A 類上面 打了 DataContract 特性的標簽,將A變成了 ?數據契約, ?B 類并沒有做任何描述的操作,也就是說B類只是一個普通類型?), B繼承了A ? ,所以 正常的情況下? 如果 ?某個方法的 ?參數 是 ?A類型,那么 傳入 B類型是不應該有錯誤的。因為 ?B是A的子類。 ? 可這里 ?WCF 并不認識B,盡管 方法的 參數 ?是A
這里WCF會報錯的,(.Net的類型可以分為兩種:聲明類型和真實類型。我們提倡面向接口的編程,對象的真實類型往往需要在運行時才能確定,在編程的時候只需要指明類型的聲明類型,比如類型實現的接口或抽象類。)當我們使用基于接口或抽象類(類)創建
?
的DataContractSerializer(數據契約)去序列化一個實現了該接口或繼承該抽象類的實例時,往往會因為無法識別對象的真實類型造成不能正常的序列化。
?
所以客戶端 去調用方法并傳入A類型的子類 類型 作為參數 ? 也是會報錯的。 ?因為 ?B 類型并不是 已知的類型。
?
具體應該怎么做呢?那這種情況下 我們就應該 ?將 " 未知?" 類型加入DataContractSerializer " 已知?" 類型列表中。
?
那現在 就是 ?應該我們的?KnownTypeAttribute 與ServiceKnowTypeAttribute 兩個特性出場的時候了。
1.KnownTypeAttribute :
KnownTypeAttribute應用于數據契約中,用于設置繼承于該數據契約類型的子數據契約類型,或者引用其他潛在的類型。
?
2.ServiceKnowTypeAttribute :
ServiceKnowTypeAttribute既可以應用于服務契約的接口和方法上,也可以應用在服務實現的類和方法上。如果應用在服務契約類型上,未知類型的這個類型?在所有實現了該契約的服務操作中有效,如果應用于服務契約的操作方法上,則定義的未知類型在所有實現了該契約的服務對應的操作方法中有效.
?
上面的兩種方法,二選一,兩種都可以做到 將未知類型加入到 已知類型當中。
?
?
?
我們再次看Demo,云盤上有
[ 7-01 ]
?
Client:客戶端
ContentTypes:數據契約層
GigManager:服務契約層 ?( 這里 把服務契約的 實現 也寫到了 這里。當然 也可以把它分開?)
Hosting:服務器寄宿
?
?
首先看 ?GigManager:服務契約層 ?
[ 7-02 ]
?
?
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.ServiceModel; 6 using ContentTypes; 7 8 namespace GigManager 9 { 10 11 [ServiceContract] 12 public interface IGigManagerService 13 { 14 15 [OperationContract] 16 //[ServiceKnownType(typeof(Order))] 17 void Save(OrderBase order); 18 } 19 20 public class GigManagerService : IGigManagerService 21 { 22 public void Save(OrderBase order) 23 { 24 25 } 26 } 27 28 }?
?
然后 看 ?ContentTypes:數據契約層
[ 7-03 ]
?
?
[ 7-04 ]
?
OrderBase.cs
?
?
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.ServiceModel; 6 using System.Runtime.Serialization; 7 namespace ContentTypes 8 { 9 10 [DataContract] 11 //[KnownType(typeof(Order))] 12 public class OrderBase 13 { 14 [DataMember] 15 public Guid ID { get; set; } 16 17 [DataMember] 18 public DateTime Date { get; set; } 19 20 [DataMember] 21 public string Customer { get; set; } 22 23 [DataMember] 24 public string ShipAddress { get; set; } 25 } 26 }?
[ 7-05 ]
Order.cs
?
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Runtime.Serialization; 6 7 namespace ContentTypes 8 { 9 [DataContract] 10 public class Order : OrderBase 11 { 12 [DataMember] 13 public double TotalPrice { get; set; } 14 } 15 16 }?
?
可以看到數據契約層中有 兩個 類,一個OrderBase 為父類, 一個?Order ?為?OrderBase 的子類
?
接下來看 ?Hosting:服務器寄宿( 這里的代碼沒有什么改變與第六講一樣?)
?
代碼
[ 7-06 ]
?
?
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.ServiceModel; 6 7 namespace Hosting 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 using (ServiceHost host = new ServiceHost(typeof(GigManager.GigManagerService))) 14 { 15 host.Open(); 16 Console.WriteLine(); 17 Console.WriteLine("服務已經啟動"); 18 Console.ReadLine(); 19 } 20 } 21 } 22 }?
appconfig 配置:
[ 7-07 ]
?
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <system.serviceModel> 4 <services> 5 <service name="GigManager.GigManagerService" behaviorConfiguration="serviceBehavior"> 6 <endpoint address="GigManagerService" contract="GigManager.IGigManagerService" binding="netTcpBinding"></endpoint> 7 <endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex"></endpoint> 8 <host> 9 <baseAddresses> 10 <add baseAddress="http://127.0.0.1:8000"/> 11 <add baseAddress="net.tcp://127.0.0.1:9000"/> 12 </baseAddresses> 13 </host> 14 </service> 15 </services> 16 <behaviors> 17 <serviceBehaviors> 18 <behavior name="serviceBehavior"> 19 <serviceMetadata httpGetEnabled="true"/> 20 </behavior> 21 </serviceBehaviors> 22 </behaviors> 23 </system.serviceModel> 24 </configuration>?
?
?
?
最后看?Client:客戶端
?
代碼
[ 7-08 ]
?
?
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using Client.localhost; 6 using ContentTypes; 7 8 namespace Client 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 using (localhost.GigManagerServiceClient proxy = new Client.localhost.GigManagerServiceClient()) 15 { 16 Order order = new Order(); 17 order.ID = Guid.NewGuid(); 18 order.Customer = "NCS"; 19 order.Date = DateTime.Today; 20 order.ShipAddress = "sssssssssss"; 21 order.TotalPrice = 888.8; 22 proxy.Save(order); 23 Console.WriteLine(order.ID.ToString()); 24 Console.WriteLine(order.Customer); 25 Console.Read(); 26 } 27 } 28 } 29 }?
?
appconfig 配置:?( 這里的代碼沒有什么改變與第六講一樣?)
這里的配置 ?是我們 添加了 ?服務引用 ?自動生成 的 ,可以不管
[ 7-09 ]
?
?
?
?
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <system.serviceModel> 4 <bindings> 5 <netTcpBinding> 6 <binding name="NetTcpBinding_IGigManagerService" closeTimeout="00:01:00" 7 openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" 8 transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" 9 hostNameComparisonMode="StrongWildcard" listenBacklog="10" 10 maxBufferPoolSize="524288" maxBufferSize="65536" maxConnections="10" 11 maxReceivedMessageSize="65536"> 12 <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" 13 maxBytesPerRead="4096" maxNameTableCharCount="16384" /> 14 <reliableSession ordered="true" inactivityTimeout="00:10:00" 15 enabled="false" /> 16 <security mode="Transport"> 17 <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" /> 18 <message clientCredentialType="Windows" /> 19 </security> 20 </binding> 21 </netTcpBinding> 22 </bindings> 23 <client> 24 <endpoint address="net.tcp://127.0.0.1:9000/GigManagerService" 25 binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IGigManagerService" 26 contract="localhost.IGigManagerService" name="NetTcpBinding_IGigManagerService"> 27 <identity> 28 <userPrincipalName value="20080904-1145\Administrator" /> 29 </identity> 30 </endpoint> 31 </client> 32 </system.serviceModel> 33 </configuration>?
我們仔細看 這里 ,這里的 ?需要的參數是OrderBase類型,而我們傳入的是OrderBase ?的 子類Order 類型,所以這里一定會出錯。
[ 7-10 ]
?
運行后 ?的報錯
[ 7-11 ]
?
錯誤消息:
格式化程序嘗試對消息反序列化時引發異常: 嘗試對參數 http://tempuri.org/ 進行反序列化時出錯: order。InnerException 消息是“元素“http://tempuri.org/:order”含有“http://schemas.datacontract.org/2004/07/ContentTypes:Order”數據協定的數據。反序列化程序不知道映射到此協定的類型。請將與“Order”對應的類型添加到已知類型的列表中,例如,通過使用 KnownTypeAttribute 屬性或通過將其添加到傳遞給 DataContractSerializer 的已知類型的列表等方法。”。有關詳細信息,請參閱 InnerException。
?
這里很清楚的告訴了 我們 ?不認識 ?Order?類型。
?
如何代碼實現?
1.使用KnownTypeAttribute?
在父類的 數據契約上 ?加上?[KnownType(typeof(Order))] 特性 , 將?Order ?這個 " 未知?" ?類型 ?加入??" 已知?" 類型列表中。
[ 7-12 ]
?
2.使用ServiceKnownType
在需要用到該未知類型 操作的服務契約上或者服務契約中的方法上 打上 [ServiceKnownType(typeof(Order))] 特性標簽 ,則定義的 ? 未知類型 在所有實現了該契約的服務對應的操作中有效.
特性打在 ?服務契約 上面
[ 7-13 ]
?
或者 特性打在 服務契約中的方法上
[ 7-14 ]
?
?
好 , 打上 特性之后 ?,我們再試一試 ,這樣就可以了。
?
轉載于:https://www.cnblogs.com/xulang/p/5495108.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的第七讲:数据契约(2)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CodeForces 671C - Ul
- 下一篇: python字符类型的一些方法