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