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

时间:2014-11-03 17:23:04

标签: c# wcf serialization datacontractserializer

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

[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使用不同的DataContractSerializer来反序列化T1。

EDIT1: 看起来这应该可以通过自定义DataContractResolver并将其注入两个(客户端 - 服务器)WCF端的Operations合同来实现。我得到了这个几乎工作 - 序列化 - 反序列化本身按预期工作,WCF仍然失败。一旦找到原因,我会尝试发布答案

2 个答案:

答案 0 :(得分:1)

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和namespace初始化为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();
    }

    ...

}

客户现在可以根据您的需要接收反序列化的类型。

答案 1 :(得分:1)

可以想到两个WCF扩展点:

  • 789派生(如您自己的答案中所述) - 允许您定义XML与其所需.Net类型之间的新匹配。 Read more here
  • 实施BEFORE: stackoverflow=# select * from insert_only; id | start_date | end_date | account ----+------------+------------+--------- 1 | 2016-08-01 | | 123 1 | 2017-08-01 | | 111 1 | 2017-04-26 | 2017-07-30 | 789 1 | 2016-08-01 | 2016-08-01 | 789 (4 rows) stackoverflow=# Insert into insert_only (ID, start_date, account) values (1,'2016-08-01',123) ON CONFLICT DO NOTHING; INSERT 0 0 stackoverflow=# Insert into insert_only (ID, start_date, end_date,account) values (1,'2016-08-01','2016-08-01',789) ON CONFLICT DO NOTHING; INSERT 0 1 AFTER: stackoverflow=# select * from insert_only; id | start_date | end_date | account ----+------------+------------+--------- 1 | 2016-08-01 | | 123 1 | 2017-08-01 | | 111 1 | 2017-04-26 | 2017-07-30 | 789 1 | 2016-08-01 | 2016-08-01 | 789 1 | 2016-08-01 | 2016-08-01 | 789 (5 rows) - 将一个.Net类型替换为另一个.Net类型以进行序列化。不涉及XML。 Read more here

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