当谈到 WCF 中的序列化和反序列化时,我面临着另一个“谜团”。让我带您了解详细信息;
- 我们有一个从 WCF 服务(由 TCP 托管)返回的预置对象 (NHibernate)。
我们有一个 DataContractSurrogate 它检查属性/集合是否是 NHibernate 代理,如果是,则执行以下操作之一:
- 如果代理已初始化(以及序列化的正确类型),则返回对象
- 如果代理未初始化,则返回 NULL
到目前为止一切顺利,DataContractSurrogate 完成了它的工作,序列化并将消息整齐地发送到客户端,在反序列化时我收到以下错误:
无法将“System.Object”类型的对象转换为“XXXXXXXXXX”类型
在这一点上,为什么它会投射一个 NULL 值?这是否意味着我不能将属性设为 NULL 这是什么疯狂?
所以我决定检查我的 DataContractSurrogate 处理 NULL 值而不是我们的代理的方式是否有任何区别。我基本上只是手动将前面提到的属性设置为 NULL,而不是让 DataContractSurrogate 为我执行此操作。
神奇的是,这行得通......这真的让我下巴掉了,所以我决定逐步了解 DataContractSurrogate 的不同方法,看看发生了什么,这就是我注意到的。
当我出于某种原因手动将属性设置为 NULL 时(很可能是因为顶部的某些层告诉它不要这样做)永远不会调用 GetObjectToSerialize。显然,当我不手动将属性设置为 NULL 时,会调用该方法并且我返回 NULL 而不是代理/初始化对象。
以下是我的 DataContractSurrogate 中的两种方法,以防任何人都感兴趣:
public Type GetDataContractType(Type type)
{
// Serialize proxies as the base type
if (typeof(INHibernateProxy).IsAssignableFrom(type))
{
type = type.GetType().BaseType;
}
// Serialize persistent collections as the collection interface type
if (typeof(IPersistentCollection).IsAssignableFrom(type))
{
foreach (Type collInterface in type.GetInterfaces())
{
if (collInterface.IsGenericType)
{
type = collInterface;
break;
}
else if (!collInterface.Equals(typeof(IPersistentCollection)))
{
type = collInterface;
}
}
}
return type;
}
public object GetObjectToSerialize(object obj, Type targetType)
{
// Serialize proxies as the base type
if (obj is INHibernateProxy)
{
ILazyInitializer init = ((INHibernateProxy)obj).HibernateLazyInitializer;
if (init.IsUninitialized)
{
obj = null;
}
else
{
obj = init.GetImplementation();
}
}
// Serialize persistent collections as the collection interface type
if (obj is IPersistentCollection)
{
//return
if (!((IPersistentCollection)obj).WasInitialized)
{
Type type = typeof(Collection<>).MakeGenericType(obj.GetType().GetGenericArguments());
obj = Activator.CreateInstance(type);
}
}
return obj;
}
我想知道的是我怎样才能让它工作,我希望能够无意识地返回我的预置对象(是的,DTO 会解决这个问题,但我不想走那条路,那就需要更多的工作来维护让它发挥作用IMO)。
任何人都对如何让这个工作有任何想法(在客户端没有调用 GetObjectToSerialize,请求类型,然后 BAM 它爆炸了)?
编辑:
这是序列化后的消息:
<OnReceiveCall xmlns="http://tempuri.org/">
<call xmlns:b="http://schemas.datacontract.org/2004/07/TelephoneExchange.Entities" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<b:Begin>2012-10-03T14:57:20</b:Begin>
**<b:CallingTelephone i:type="c:anyType" xmlns:c="http://www.w3.org/2001/XMLSchema"></b:CallingTelephone>**
<b:End i:nil="true"></b:End>
<b:HasRecording>false</b:HasRecording>
<b:ID>149688</b:ID>
<b:Inputs></b:Inputs>
<b:LeftInQueue>false</b:LeftInQueue>
<b:RecipientTelephone>
<b:IsInternal>true</b:IsInternal>
<b:Name>604</b:Name>
<b:Number>604</b:Number>
</b:RecipientTelephone>
<b:TransUniqueID i:nil="true"></b:TransUniqueID>
</call>
</OnReceiveCall>
** 和 ** 之间的位是感兴趣的属性。
当我手动使属性为空时,XML 如下所示:
<OnReceiveCall xmlns="http://tempuri.org/">
<call xmlns:d4p1="http://schemas.datacontract.org/2004/07/TelephoneExchange.Entities" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<d4p1:Begin>2012-10-03T15:04:18</d4p1:Begin>
**<d4p1:CallingTelephone i:nil="true"></d4p1:CallingTelephone>**
<d4p1:End i:nil="true"></d4p1:End>
<d4p1:HasRecording>false</d4p1:HasRecording>
<d4p1:ID>149721</d4p1:ID>
<d4p1:Inputs></d4p1:Inputs>
<d4p1:LeftInQueue>false</d4p1:LeftInQueue>
<d4p1:RecipientTelephone>
<d4p1:IsInternal>true</d4p1:IsInternal>
<d4p1:Name>604</d4p1:Name>
<d4p1:Number>604</d4p1:Number>
</d4p1:RecipientTelephone>
<d4p1:TransUniqueID i:nil="true"></d4p1:TransUniqueID>
</call>
</OnReceiveCall>
注意区别……现在的问题是……我到底是怎么做的:-)