2

当谈到 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>

注意区别……现在的问题是……我到底是怎么做的:-)

4

1 回答 1

0

问题在这里:

// Serialize proxies as the base type 
if (typeof(INHibernateProxy).IsAssignableFrom(type)) 
{ 
   type = type.GetType().BaseType; 
} 

这是一个常见的错误。 type本身是一个对象,代表您的(可能是代理)对象的类型;的类型typeSystem.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 转换为另一种类型的异常。

其次,如果一个对象为空,则obj is SomeType计算结果为false。因此,如果 GetObjectToSerialize 的第一个参数为 null,则 GetObjectToSerialize 将返回 null。您没有显示 GetDataContractType 的调用站点,因此很难看出 null 属性值将如何影响它。

编辑2:

当然,上面是不正确的,因为type.GetType()返回typeof(System.RuntimeType), nottypeof(System.Type)`。

下面是正确的分析:

这是一个常见的错误。 type本身是一个对象,代表您的(可能是代理)对象的类型;的静态类型typeSystem.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");
于 2012-10-03T10:38:25.723 回答