指示WCF将类型T1反序列化为T2

本文关键字:反序列化 T2 T1 类型 WCF 指示 | 更新日期: 2023-09-27 18:28:58

让我们有一个非常简单的例子:

[ServiceContract]
public interface IMyService
{
    [OperationContract]
    T1 GetData();
}
[DataContract]
[KnownType(typeof(T2))]
public class T1
{ 
    [DataMember] 
    int Value { get; set; }
}
[DataContract]
public class T2: T1
{ }

我需要能够发送T1,但在客户端将其作为T2接收(简化的原因:业务对象快速转换为客户端只读类型;将服务器池对象转换为客户端未激活类型。如果需要,我可以进一步详细说明)。

为了使其稍微复杂一点,发送数据的类型树有更多的类型,例如:

[OperationContract]
TBase GetData();
[DataContract]
[KnownType(typeof(T1))]
[KnownType(typeof(T2))]
[KnownType(typeof(T3))]
[KnownType(typeof(Tn))]
public class TBase
{ 
    [DataMember] 
    int Value { get; set; }
}
[DataContract]
public class T1: TBase { }
[DataContract]
public class T2: TBase { }
[DataContract]
public class T3: TBase { }
[DataContract]
public class Tn: TBase { }

我需要照原样接收所有类型,除了T1,它需要作为T2接收。

我测试了序列化/反序列化部分使用DataContractSerializer很容易实现,但我找不到如何指示WCF使用不同的DataContractSerialize来反序列化T1。

第1版:通过派生自定义DataContractResolver并将其注入(客户端-服务器)WCF双方的操作契约,这似乎是可行的。我得到了这个几乎工作-序列化反序列化本身如预期的那样工作,WCF仍然失败。一旦我找到

指示WCF将类型T1反序列化为T2

的原因,我会尝试发布答案

想到的两个WCF扩展点:

  • DataContractResolver派生(如您自己的回答中所述)-可以定义XML与其所需.Net类型之间的新匹配。点击此处阅读更多信息
  • 实现IDataContractSurrogate-将一个.Net类型替换为另一个类型以实现序列化。不涉及XML。点击此处阅读更多信息

它们都是通过DataContractSerializerOperationBehavior上的属性以类似的方式注入的。

DataContractResolver似乎在那里执行这些任务,所以我覆盖了它:

public class MyResolver : DataContractResolver
{
    private XmlDictionaryString _typeName;
    private XmlDictionaryString _typeNamespace;
    public PooledPricesResolver()
    {
        XmlDictionary dictionary = new XmlDictionary();
        _typeName = dictionary.Add("T2");
        _typeNamespace = dictionary.Add("MyNamespace");
    }
    public override bool TryResolveType(Type type, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace)
    {
        if (declaredType == typeof(T1))
        {
            typeName = _typeName; //null;
            typeNamespace = _typeNamespace; //null
            return true;
        }
        return knownTypeResolver.TryResolveType(type, declaredType, null, out typeName, out typeNamespace);
    }
    public override Type ResolveName(string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver)
    {
        if (typeName == "T2" && typeNamespace == "MyNamespace")
        {
            return typeof(T1);
        }
        return knownTypeResolver.ResolveName(typeName, typeNamespace, declaredType, null) ?? declaredType;
    }
}

请注意,这可能更复杂。或者,如果您确实需要将类型解析为基类型,那么TryResolveType可以将typeName和命名空间初始化为null,ResolveName可以只调用knownTypeResolver实现。

要在服务器端(主机)注入此解析器,您可以执行以下操作:

//_serviceHost is ServiceHost.ServiceHost type
ContractDescription cd = _serviceHost.Description.Endpoints[0].Contract;
//the string is the name of operation for which you can do the custom (de)serialization
cd.Operations.Find("GetData")
DataContractSerializerOperationBehavior serializerBehavior = operation.Behaviors.Find<DataContractSerializerOperationBehavior>();
if (serializerBehavior == null)
{
    serializerBehavior = new DataContractSerializerOperationBehavior(operation);
    operation.Behaviors.Add(serializerBehavior);
}
serializerBehavior.DataContractResolver = new MyResolver();
//Now you can start listening by _serviceHost.Open()

在客户端,如果您使用手工代理,您可以执行以下操作:

public class MyServiceProxy : System.ServiceModel.DuplexClientBase<IMyService>, IMyService
{
    public MyServiceProxy(InstanceContext callbackInstance, Binding binding, EndpointAddress remoteAddress)
        : base(callbackInstance, binding, remoteAddress)
    {
        ContractDescription cd = this.Endpoint.Contract;
        //the string is the name of operation for which you can do the custom (de)serialization
        cd.Operations.Find("GetData")
        DataContractSerializerOperationBehavior serializerBehavior = operation.Behaviors.Find<DataContractSerializerOperationBehavior>();
        if (serializerBehavior == null)
        {
            serializerBehavior = new DataContractSerializerOperationBehavior(operation);
            operation.Behaviors.Add(serializerBehavior);
        }
        serializerBehavior.DataContractResolver = new MyResolver();
    }
    ...
}

客户端现在将能够接收根据您的需要反序列化的类型。