WCF:返回与基础对象(DataContractResolver)的合同的派生对象

时间:2011-12-06 15:22:04

标签: wcf datacontract

我有WCF派生/基本合同问题。我有一个返回BaseThing对象的服务器接口/契约:

[OperationContract]
BaseThing Get_base_thing();

实现此功能的服务器具有DerivedThing(源自BaseThing),并希望将其作为BaseThing返回。 如何告诉WCF我只想传输BaseThing的{​​{1}}部分?

如果在DerivedThing中,我只返回对Get_base_thing的引用,那么我会得到一个DerivedThing服务器端。

我想我需要定义一个SerializationException,然后我查看了MSDN文章Using a Data Contract Resolver,但这并非100%明确(至少对我而言)。

我的DataContractResolver应该如何告诉WCF仅传输我传递的派生对象的基本部分?

是否有一些方法可以更简单地使用DataContractResolver attribue?

2 个答案:

答案 0 :(得分: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之类的显式转换,因此在您的操作中很明显,服务器上使用的对象与暴露给外部世界的对象之间存在差异。

答案 1 :(得分:1)

发布后我也发现了这个相同的问题How to serialize a derived type as base。 marc对我来说不可接受的第二个答案是解决此问题的最简单方法。那就是:
使用 [DataContract(Name="BaseClass")]装饰派生类 请注意,此解决方案意味着派生将作为此对象的所有传输的基础传输。对我来说,这不是问题,如果是,那么你需要去DataContractResolver路线。

关于DataContractResolver路线的一些注意事项:
1.这使您可以在某些调用中传递派生的派生,但作为基于其他调用的派生 - 如果您需要这样做 - 如果不使用关于Name = approach。
2.我使用datacontractrsolver文章中的DeserializeAsBaseResolver得到一个异常,因为knownTypeResolver返回false。为了解决这个问题,我忽略了该调用的返回值,并始终从TryResolveType返回true。这似乎有效。
3.我最初认为,因为我们序列化为基础,我不需要派生类的[DataContract]。那是错的。该对象被序列化为派生对象并被动态化为基础对象 - 因此您必须使用[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(); }