指示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扩展点:
- 从
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();
}
...
}
客户端现在将能够接收根据您的需要反序列化的类型。