再说WCF Data Contract KnownTypeAttribute
?WCF?中的序列化是用DataContractSerializer,所有被[DataContract]和[DataMemeber]標記的類和屬性會被DataContractSerializer序列化。在WCF中使用Contract模式來分辨和指定序列化/反序列化的類型,它是通過http://xmlns/Class這樣的命名空間來標識這個序列化的對象的,一旦在序列化過程中無法找到這樣的標識(比如某個字段,或者子對象)時,序列化就會失敗。KnownTypeAttribute就提供了為我們通知序列化器去尋找未知對象的映射的途徑。在Remoting中這樣的問題不會存在,因為Remoting實際上是通過將一個類型傳遞給雙方來進行類型匹配的。?
?????????那么KnowTypeAttribute到底用在什么地方呢?上邊說了,當前類的未知類型。那什么又是當前類的未知類型呢?或許說未知類型有些不恰當,但下邊的實際應用可能會讓你更清楚這到底是指什么。
1).?序列化對象是從期望返回的類型繼承;
2).?無法確定當前所使用類型的。例如Object類型,或者接口類型,你需要告訴序列化器去尋找確切的類來進行序列化。
3).?使用泛型類型作為期望返回類型的;
4).?使用像ArrayList等以object為基礎類型存儲對象的;?
??????????下邊我們以第一種類型為例說明KnownTypeAttribute的用法。序列化對象一般是參與到在服務端和客戶端傳遞的數據。在面向對象的設計中,繼承可以很好的解決很多業務問題,并簡化處理。而在下邊的例子中我們可以看出KnownType到底能夠做什么。
| namespace?WcfServiceDemo { ????[DataContract] ????public?class?Address ????{ ????????public?Address() ????????{ ?????????????…… ????????} ? ????????[DataMember] ????????public?string?AddressCategory {?get;?set; } ? ????????[DataMember] ????????public?string?AddressTitle {?get;?set; } ? ????????[DataMember] ????????public?string?AddressDetail {?get;?set; } ????} ? ????[DataContract] ????public?class?BusinessAddress?:?Address ????{ ????????…… ????} ? ????[DataContract] ????public?class?HomeAddress?:?Address ????{ ????????…… ????} } ? public?class?Service1?:?IService1 { ????public?Address?GetAddress(bool?isHome) ????{ ????????if?(isHome) ????????????return?new?HomeAddress(); ????????else ????????????return?new?BusinessAddress(); ????} } |
?
?????????上邊的代碼中簡單的生命了三個數據契約類型,Address作為基類,HomeAddress和BusinessAddress繼承于Address類。在Service實現中,簡單的通過一個參數來判斷到底返回哪個子類。我們可以簡單的寫個客戶端來調用一下這個服務,但很快你會發現瀏覽器(客戶端)返回給你了錯誤:
Server Error in '/' Application.
The underlying connection was closed: The connection was closed unexpectedly.
Description:?An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.?
Exception Details:?System.Net.WebException: The underlying connection was closed: The connection was closed unexpectedly.
?
????????很顯然,這個錯誤并不能清楚的反應具體發生了什么,如果你跟進服務你會發現,所有的操作都正常,只是在返回數據的時候發生了錯誤。如果你愿意,你可以用DataContractSerializer類的一些方法來將一個HomeAddress序列化并反序列化為一個Address類型,你就會看到這個過程中發生了錯誤,序列化都無法進行。為什么呢?原因是HomeAddress/BusinessAddress的類型標識(如http://www.demo.com/BusinessAddress)無法序列化Address類型時匹配,它就不知道該把它序列化成哪個確切的類型。?
????????解決方法,給Address添加KnownTypeAttribute標識,當一個HomeAddress對象或者BusinessAddress對象被傳遞到Address進行序列化時,序列化器認識這個對象并能根據契約來進行序列化。
| [DataContract] [KnownType(typeof(BusinessAddress))] [KnownType(typeof(HomeAddress))] public?class?Address { ??????…… } |
?
??? 再次調用客戶端(注意:如果你是通過Service Reference來引用服務的,那你必須在編譯完服務端后選擇Update Service Reference來更新服務引用,否則你的變化不會反應到客戶端調用),現在你應該可以看到結果了。對于KnownTypeAttribute它還有一個可以替換的選擇ServiceKnownTypeAttribute,你可以將它應用到一個Service或者一個Operation(他們的區別是:當把ServiceKnownType標記給以個Service,那么在這個Service內KnownType都發生作用;而對于Operation則只在這個Operation時有效,即作用域不同。)。以下代碼同樣解決了上述問題:
| [OperationContract] [ServiceKnownType(typeof(BusinessAddress))] [ServiceKnownType(typeof(HomeAddress))] Address?GetAddress(bool?isHome); |
?
??? KnownType的另外一種標識方式是不用加代碼的,在配置文件中通過聲明型編程實現:
| <system.runtime.serialization> ??<dataContractSerializer> ????<declaredTypes> ??????<add?type="WcfServiceDemo.Address, MyAssembly, Version=2.0.0.0, Culture=neutral,PublicKeyToken=null, processorArchitecture=MSIL"> ????????<knownType?type="MyCompany.Library.Circle, MyAssembly, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null, processorArchitecture=MSIL"/> ??????</add> ????</declaredTypes> ??</dataContractSerializer> </system.runtime.serialization> |
?
??? 而對于泛型來說,KnownType是不能被直接應用的,如果你寫成[KnownType(typeof(BusinessAddress<>))]這樣是不允許,但CLR給我們提供了另外一種方法來實現對序列化的通知:
| [KnownType("GetKnownTypes")] public?class?Address<T> {??????? ????static?Type[] GetKnownTypes() ????{ ????????return?new?Type[] {?typeof(HomeAddress<T>) ,typeof(BusinessAddress)}; ????} } |
??? 我們指定尋找KnownType時到GetKnownTypes方法去找,GetKnownTypes方法返回一個告知序列化器映射類型的數組。既然我們可以用方法,那是否意味著我們也可以動態的來定義GetKnownTypes返回的類型集合呢?當然,在上邊的例子中你可以通過擴展GetKnownTypes方法來實現。但為了DataContract類型定義的簡約和獨立性,我們不妨將這個函數摘出來,或許更有利于程序結構:
| [ServiceContract] [ServiceKnownType("GetKnownTypes",?typeof(KnownTypesProvider))]??? public?interface?IService1 { ????…… } ? static?class?KnownTypesProvider { ????static?Type[] GetKnownTypes(ICustomAttributeProvider?knownTypeAttributeTarget) ????{ ????????Type?contractType = (Type)knownTypeAttributeTarget; ????????return?contractType.GetGenericArguments() ; ????} } |
?
??? 我們將類型通知提供者定義到函數外部,并通過利用ServiceKnownType的重載構造函數傳入需要去尋找的通知類提供者及其方法。這樣你很容易擴展這個函數并動態加載通知類型。
?????????你仍然可以通過使用配置文件的方式來達到同樣的功能:
| ??<system.runtime.serialization> ????<dataContractSerializer> ??????<declaredTypes> ????????<add?type="MyCompany.Library.Shape, ??????????????MyAssembly, Version=2.0.0.0, Culture=neutral, ??????????????PublicKeyToken=XXXXXX, processorArchitecture=MSIL"> ??????????<knownType?type="MyCompany.Library.Circle, ???????????????????????MyAssembly, Version=2.0.0.0, Culture=neutral, ???????????????????????PublicKeyToken=XXXXXX, processorArchitecture=MSIL"> ????????????<parameter?type="System.Collections.Generic.Dictionary"> ??????????????<parameter?type="System.String"/> ??????????????<parameter?index="0"/> ????????????</parameter> ??????????</knownType> ????????</add> ??????</declaredTypes> ????</dataContractSerializer> ??</system.runtime.serialization> |
?
??? 以上代碼就指定了將Circle<Dictionary<string, T>>作為Shape<T>的一個knownType。注意,當你指定某個knownType可能用到多于一個參數為泛型對象的參數時,通過index來指定與期望類型T不同的另外一個類型。例如上邊的代碼中指定了第一個參數為String類型(通過index指定),第二個參數T與Shape<T>的T相同,不用特別指定。
轉載于:https://www.cnblogs.com/sjqq/p/6782538.html
總結
以上是生活随笔為你收集整理的再说WCF Data Contract KnownTypeAttribute的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 为什么总梦到死人
- 下一篇: TAppEncoder的main函数