2011-12-06 106 views
1

我有一個WCF派生/基礎合同問題。我有一臺服務器接口/合同返回一個BaseThing對象:WCF:爲基礎對象返回派生對象(DataContractResolver)

[OperationContract] 
BaseThing Get_base_thing(); 

實現這個有着DerivedThing(從BaseThing派生),並希望返回以此爲BaseThing服務器。 如何告訴WCF我只想運輸部分DerivedThing

如果在Get_base_thing我只是返回一個參考DerivedThing然後我得到一個SerializationException服務器端。

我想我需要定義一個DataContractResolver,我看了一下MSDN文章Using a Data Contract Resolver,但這並不是100%清楚的(對我來說至少是這樣)。

我的DataContractResolver應該如何告訴WCF只傳輸派生對象的基礎部分?

有沒有辦法做到這一點更簡單只是與KnownType屬性?

回答

1

張貼後我還發現這個SO相同的問題How to serialize a derived type as base。 marc對我的未被接受的第二個答案是解決這個問題的最簡單的方法。那就是:
裝飾派生類與[DataContract(Name="BaseClass")]
請注意,此解決方案意味着衍生將作爲基礎運輸該對象的傳輸的所有每一種情況下。對於我來說,如果它不是問題,那麼你需要去DataContractResolver路由。

的DataContractResolver路線上的一些注意事項:
1.這使您可以通過導出一些呼叫衍生而來,但作爲基礎的其他 - 如果你需要做到這一點 - 如果不是名稱=方式使用。
2.我從datacontractrsolver文章中使用DeserializeAsBaseResolver得到一個異常,因爲knownTypeResolver返回false。爲了解決這個問題,我忽略了該調用的返回值,並始終從TryResolveType返回true。這似乎工作。 3 3。我最初認爲,因爲我們序列化爲基礎,我並不需要派生類上的[DataContract]。那是錯誤的。該對象被序列化爲派生對象並作爲基礎對象進行derserialized - 因此您必須用[DataContract]修飾派生,但不要將任何字段標記爲[DataMembers]以避免它們被不必要地序列化。
4.如果您有命令行主機和服務主機,則需要代碼在兩者中插入合約解析器。我發現將它作爲靜態解析器很有用。
5.請注意,cd.Operations.Find("Get_gateway_data")調用中的「Get_gateway_data」字符串是返回相關對象的合同方法的名稱。您需要爲每個需要此行爲的呼叫執行此操作。

這種方法最終代碼:

public class DeserializeAsBaseResolver : DataContractResolver { 

    public static void Install(ServiceHost service_host) { 
     // Setup DataContractResolver for GatewayProcessing to GatewayData resolution: 
     ContractDescription cd = service_host.Description.Endpoints[0].Contract; 
     OperationDescription myOperationDescription = cd.Operations.Find("Get_gateway_data"); 
     DataContractSerializerOperationBehavior serializerBehavior = myOperationDescription.Behaviors.Find<DataContractSerializerOperationBehavior>(); 
     if (serializerBehavior == null) { 
      serializerBehavior = new DataContractSerializerOperationBehavior(myOperationDescription); 
      myOperationDescription.Behaviors.Add(serializerBehavior); 
     } 
     serializerBehavior.DataContractResolver = new DeserializeAsBaseResolver(); 
    } 

    public override bool TryResolveType(Type type, Type declaredType, 
             DataContractResolver knownTypeResolver, 
             out XmlDictionaryString typeName, 
             out XmlDictionaryString typeNamespace) { 

     bool ret = knownTypeResolver.TryResolveType(type, declaredType, null, out typeName, out typeNamespace); 
     //return ret; // ret = false which causes an exception. 
     return true; 
    } 

    public override Type ResolveName(string typeName, string typeNamespace, 
            Type declaredType, DataContractResolver knownTypeResolver) { 

     return knownTypeResolver.ResolveName(typeName, typeNamespace, declaredType, null) ?? declaredType; 
    } 

主機代碼(服務或命令行):

using (ServiceHost service_host = new ServiceHost(typeof(GatewayServer))) { 

// Setup DataContractResolver for GatewayProcessing to GatewayData resolution: 
DeserializeAsBaseResolver.Install(service_host); 

// Open the host and start listening for incoming messages. 
try { service_host.Open(); } 
3

KnownType無法解決此問題。

聽起來好像您在服務器上使用的對象模型與您使用的服務契約之間存在嚴重的分歧。似乎有3種可能的解決方案:

1)數據合同解析器,如您所識別的,使其自動跨所有操作。這裏有很多例子,包括這一個:http://blogs.msdn.com/b/youssefm/archive/2009/06/05/introducing-a-new-datacontractserializer-feature-the-datacontractresolver.aspx

2)對齊您的對象模型以更好地匹配您的服務合同。也就是說,使用遏制而不是繼承來管理BaseThing-DerivedThing關係。這樣,您可以在服務器上使用DerivedThing,並通過線路簡單地返回DerivedThing.BaseThing。如果BaseThing需要從客戶端傳輸到服務器,這也會更好。

3)使用類似於AutoMapper的顯式轉換,所以在您的操作中顯而易見的是在服務器上使用的對象與暴露給外部世界的對象之間存在分歧。

+0

我已經改變了接受下面,因爲我想強調的名稱我自己的答案= 「BaseClass」評價,這是我真正想要的。感謝您的輸入。 KnownType的+1將無法解析。 – Ricibob

相關問題