2

WCFでのシリアル化と逆シリアル化に関しては、さらに別の「謎」に直面しています。詳細を説明します。

  • WCFサービス(TCPでホストされている)から返される既存のオブジェクト(NHibernate)があります。
  • プロパティ/コレクションがNHibernateプロキシであるかどうかをチェックするDataContractSurrogateがあり、そうである場合は、次のいずれかを実行します。

    • プロキシが初期化されている場合(およびシリアル化の正しいタイプ)、オブジェクトを返します
    • プロキシが初期化されていない場合はNULLを返します

これまでのところ、DataContractSurrogateはその役割を果たし、シリアル化してメッセージをクライアントに適切に送信します。クライアントでは、逆シリアル化時に次のエラーが発生します。

タイプ「System.Object」のオブジェクトをタイプ「XXXXXXXXXX」にキャストできません

この時点で、なぜそれはNULL値をキャストしているのですか?これは、プロパティをNULLにすることができないことを意味しますか?この狂気とは何ですか?

そこで、DataContractSurrogateがプロキシではなくNULL値を処理する方法に違いがあるかどうかを確認することにしました。基本的に、DataContractSurrogateにこれを行わせるのではなく、前述のプロパティを手動でNULLに設定します。

魔法のように、これは機能します...これは本当に私の顎を落とします、それで私はDataContractSurrogateのさまざまなメソッドをステップスルーして何が起こっているかを確認することにしました、そしてここに私が気づいたものがあります。

なんらかの理由でプロパティを手動でNULLにすると(おそらく、最上位のレイヤーがそうしないように指示しているため)、GetObjectToSerializeが呼び出されることはありません。明らかに、プロパティを手動でNULLに設定しない場合、メソッドが呼び出され、プロキシ/初期化されたオブジェクトの代わりにNULLが返されます。

誰にとっても興味深い場合に備えて、DataContractSurrogateの2つのメソッドを次に示します。

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>

**と**の間のビットは対象のプロパティです。

プロパティを手動で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>

違いに注意してください....今の質問は...どうやってこれを機能させるのですか:-)

4

1 に答える 1

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 を別の型にキャストするという例外は発生しません。

第 2 に、オブジェクトが null の場合、 は にobj is SomeType評価されfalseます。したがって、最初の引数が null の場合、GetObjectToSerialize は null を返します。GetDataContractType の呼び出しサイトを示していないため、null プロパティ値がどのように影響するかを確認するのは困難です。

編集2:

もちろん、typeof(System.Type)`をtype.GetType()返すため、上記は正しくありません。typeof(System.RuntimeType), not

正しい分析は次のとおりです。

これはよくある間違いです。 typeそれ自体は、(おそらくプロキシ) オブジェクトのタイプを表すオブジェクトです。の静的タイプtypeSystem.Type; 実行時のタイプはおそらくSystem.RuntimeType. したがって、type.GetType()戻り値typeof(System.RuntimeType)の基本型は ですSystem.Reflection.TypeInfo。したがって、コードSystem.Reflection.TypeInfoは、プロキシの基本型のインスタンスではなく、null 参照をシリアル化します。

GetDataContractType の最後にある return ステートメントの直前に次の行を追加して、何が起こるかを確認してください。

if (typeof(System.Type).IsAssignableFrom(type))
    throw new Exception("oops");
于 2012-10-03T10:38:25.723 に答える