IDataContractSurrogate,NHibernate和归零代理对象

时间:2012-10-03 10:19:49

标签: c# wcf nhibernate

在WCF中进行序列化和反序列化时,我又遇到了另一个“谜”。让我带您了解详情;

  • 我们有一个预先存在的对象(NHibernate),我们将从它返回 WCF服务(由TCP托管)。
  • 我们有一个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,请求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>

注意区别......现在问题是......我该怎么做才能做到这一点: - )

2 个答案:

答案 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

我也想找到一个解决方案,但到目前为止还没有。