在WCF中进行序列化和反序列化时,我又遇到了另一个“谜”。让我带您了解详情;
我们有一个DataContractSurrogate 检查属性/集合是否是NHibernate代理,如果是的话 以下之一:
到目前为止,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,请求Type,然后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>
注意区别......现在问题是......我该怎么做才能做到这一点: - )
答案 0 :(得分:0)
问题在于:
// Serialize proxies as the base type
if (typeof(INHibernateProxy).IsAssignableFrom(type))
{
type = type.GetType().BaseType;
}
这是一个常见的错误。 type
本身就是一个对象,表示你的(可能是代理)对象的类型; type
的类型为System.Type
。因此,type.GetType()
会返回typeof(System.Type)
,其基本类型为object
。因此,您的代码会创建object
的实例,而不是代理的基本类型的实例。
假设您确实希望将代理序列化为基本类型,则需要:
// Serialize proxies as the base type
if (typeof(INHibernateProxy).IsAssignableFrom(type))
{
type = type.BaseType;
}
编辑:
Null在这里可能是红鲱鱼。
首先,允许将null转换为任何引用类型; null的类型不是System.Object
,因此强制转换null不会导致将System.Object转换为其他类型的异常。
其次,如果对象为null,则obj is SomeType
计算为false
。因此,如果第一个参数为null,则GetObjectToSerialize将返回null。您没有显示GetDataContractType的调用站点,因此很难看到null属性值将如何影响它。
编辑2:
当然,上面的内容不正确,因为type.GetType()
返回typeof(System.RuntimeType), not
typeof(System.Type)`。
这是正确的分析:
这是一个常见的错误。 type
本身就是一个对象,表示你的(可能是代理)对象的类型; type
的静态类型为System.Type
;运行时类型很可能是System.RuntimeType
。因此,type.GetType()
会返回typeof(System.RuntimeType)
,其基本类型为System.Reflection.TypeInfo
。因此,您的代码序列化了一个空System.Reflection.TypeInfo
引用,而不是代理基类型的实例。
尝试在GetDataContractType末尾的return语句之前添加此行,以查看会发生什么:
if (typeof(System.Type).IsAssignableFrom(type))
throw new Exception("oops");
答案 1 :(得分:0)
根据:IDataContractSurrogate.GetObjectToSerialize
此方法不能返回null,因为在反序列化数据时 将被强制转换为Object类型并抛出InvalidCastException
我也想找到一个解决方案,但到目前为止还没有。