c# - IDataContractSurrogate、NHibernate 和无效代理对象

标签 c# wcf nhibernate

当谈到 WCF 中的序列化和反序列化时,我面临着另一个“谜团”。让我向您详细介绍一下;

  • 我们有一个预留对象(NHibernate),我们从一个 WCF 服务(由 TCP 托管)。
  • 我们有一个 DataContractSurrogate,它 检查属性/集合是否是 NHibernate 代理,如果是的话 以下其中一项:

    • 如果代理已初始化(以及正确的序列化类型),则返回对象
    • 如果代理未初始化,则返回 NULL

到目前为止一切顺利,DataContractSurrogate 完成了它的工作,序列化消息并将消息整齐地发送到客户端,反序列化时出现以下错误:

Unable to cast object of type 'System.Object' to type '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>

** 和 ** 之间的位是感兴趣的属性。

当我手动将属性设为 null 时,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>

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

最佳答案

问题出在这里:

// 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 转换为另一种类型的异常。

其次,如果一个对象为空,那么obj is SomeType评估为 false .因此,如果 GetObjectToSerialize 的第一个参数为 null,则将返回 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 .因此,您的代码序列化了一个 null System.Reflection.TypeInfo引用而不是代理基类型的实例。

尝试在 GetDataContractType 末尾的 return 语句之前立即添加此行,看看会发生什么:

if (typeof(System.Type).IsAssignableFrom(type))
    throw new Exception("oops");

关于c# - IDataContractSurrogate、NHibernate 和无效代理对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12706452/

相关文章:

sql-server - 删除大量行非常慢 - SQL Server

c# - 使用 key 的可逆洗牌算法

java - 在 Android 中通过 SSL 使用 WCF 服务

c# - SQL : To split or not to split? 中的邮件表

c# - HTTP POST 到 WCF 服务

.net - 在 WCF 中使用 x-www-form-urlencoded 内容类型

nhibernate - nhibernate 中的多个唯一键

asp.net-mvc - 从 asp.net POST 操作方法更新休眠实体的正确方法是什么?

c# - 轮询 MSSQL 表的替代方法

c# - Storyboard DoubleAnimation 不适用于 StackPanel 高度属性