2011-12-06 105 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

相关问题