WCF:返回具有基础对象的合同的派生对象(DataContractResolver) [英] WCF: Returning a derived object for a contract with base object (DataContractResolver)

查看:136
本文介绍了WCF:返回具有基础对象的合同的派生对象(DataContractResolver)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have have a WCF derived/base contract issue. I have a server interface/contract that returns a BaseThing object:

[OperationContract]
BaseThing Get_base_thing();

实现此功能的服务器具有DerivedThing(派生自BaseThing),并希望将其作为BaseThing返回. 如何告诉WCF我只想传输DerivedThingBaseThing部分?

The server that implements this has a DerivedThing (derived from BaseThing) and wants to return this as a BaseThing. How to tell WCF that I only want to transport the BaseThing part of DerivedThing?

如果在Get_base_thing中我只返回了对DerivedThing的引用,那么我得到了SerializationException服务器端.

If in Get_base_thing I just return a reference to a DerivedThing then I get a SerializationException server side.

我认为我需要定义一个DataContractResolver,然后查看了MSDN文章使用数据合同解析器,但这并不是100%清晰(至少对我而言).

I think I need to define a DataContractResolver and I looked at the MSDN article Using a Data Contract Resolver but this is not 100% clear (to me a least).

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

How should my DataContractResolver look to tell WCF to only transport the base part of the derived object I pass it?

是否有某种方法可以更简单地使用KnownType属性?

Is there some way to do this more simply just with KnownType attribue?

推荐答案

发布后,我还发现了同样的问题

After posting I also found this SO identical question How to serialize a derived type as base. The unaccepted second answer by marc for me is the easiest way to resolve this issue. That is:
Decorate the derived class with [DataContract(Name="BaseClass")]
Note that this solution means that derived will transport as base for all every case of transport of this object. For me that was not an issue if it is then you need to go the DataContractResolver route.

有关DataContractResolver路线的一些说明:
1.这样一来,您就可以传递在某些调用中派生而在其他调用中派生的派生对象,如果需要,可以将其作为基础(如果不使用Name =方法的话).
2.我从datacontractrsolver文章中获取使用DeserializeAsBaseResolver的异常,因为knownTypeResolver返回false.为了解决这个问题,我忽略了该调用的返回值,并始终从TryResolveType返回true.这似乎可行.
3.我最初以为,因为我们将序列化为基础,所以我不需要派生类上的[DataContract].那是错的.该对象被序列化为派生对象,并被反序列化为基础对象-因此,您必须使用[DataContract]装饰派生对象,但不要将任何字段标记为[DataMembers],以避免不必要地对其进行序列化.
4.如果您有命令行主机和服务主机,则需要代码在两者中插入合同解析器.我发现将其作为静态变量放入解析器很有用.
5.请注意,对cd.Operations.Find("Get_gateway_data")的调用中的"Get_gateway_data"字符串是返回有关对象的协定方法的名称.您需要为每个需要此行为的呼叫执行此操作.

Some notes on the DataContractResolver route:
1. This enables you to pass the derived as derived on some calls but as base on other - if you need to do that - if not use about Name= approach.
2. I get an exception using the DeserializeAsBaseResolver from the datacontractrsolver article as it stands because the knownTypeResolver returns false. To fix that I ignor the return value of that call and always return true from TryResolveType. That seems to work.
3. I initially thought that because we were serializing as base that I didnt need [DataContract] on the derived class. That was wrong. The object is serialized as the derived object and derserialized as a base object - so you must decorate the derived with [DataContract] but don't mark any fields as [DataMembers] to avoid them being unnecessarily serialize.
4. If you have a command line host and a service host then you need the code to insert the contract resolver in both. I found it useful to put this as a static in my resolver.
5. Note that that the "Get_gateway_data" string in the call to cd.Operations.Find("Get_gateway_data") is the name of the contract method that returns the object concerned. You will need to do this for each call that you want this behaviour.

此方法的最终代码:

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;
    }

主机代码(服务或命令行):

Host code (service or command line):

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(); }

这篇关于WCF:返回具有基础对象的合同的派生对象(DataContractResolver)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆