我们有一个非常简单的例子:
[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仍然失败。一旦找到原因,我会尝试发布答案
答案 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
上的属性注入。