2012-10-03 2 views
2

WCF의 직렬화 및 비 직렬화에 관해서는 또 다른 "신비"에 직면 해 있습니다. 자세한 내용을 안내하겠습니다. IDataContractSurrogate, NHibernate 및 nulling 프록시 객체

  • 우리는 presisted 객체 우리가 (TCP 주최)를 WCF 서비스에서 반환하는 (NHibernate에)가 있습니다.
  • 우리는 속성/컬렉션 검사 NHibernate에 프록시하고 경우 DataContractSurrogate이 너무 다음 중 하나를 수행하는 경우 :

    • 돌아 프록시가 초기화되면 객체 (직렬화에 대한 올바른 유형을)
    • NULL을 반환는 프록시는 DataContra, 지금까지 너무 좋아

을 초기화되어 있지 않은 경우 ctSurrogate는 직렬화,이 작업의 수행 및 직렬화 복원 할 때 나는 다음과 같은 오류가 클라이언트에 깔끔하게 메시지를 보냅니다

'XXXXXXXXXX'

입력 유형 '으로 System.Object'의 개체를 캐스팅 할 수 없습니다

이 시점에서 도대체 왜 NULL valua를 캐스팅합니까? 이것은 내가이 광기가 무엇인지를 NULL로 만들 수 없다는 것을 의미합니까?

그래서 DataContractSurrogate가 프록시 대신 NULL 값을 처리하는 방식에 차이가 있는지 확인하기로했습니다. 기본적으로 DataContractSurrogate가 나를 위해 대신이 NULL 대신 수동으로 앞에서 언급 한 속성을 설정합니다.

Magically, this works ... 정말 내 턱을 떨어 뜨리므로, 나는 무슨 일이 일어나고 있는지를 알기 위해 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; 
    } 

은 무엇 궁금하고있어 내가이 작업을 얻을 수있는 방법이다, 나는 아무 생각없이 내 presisted를 반환 할 수 있도록하려면 개체 (예, 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> 

사이의 비트 **와 ** 관심의 속성입니다.도대체 내가

:-)이 일을 어떻게 ...

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

주 차이 .... 이제 질문은 : 내가 수동으로 속성을 null로 할 때

는 XML은 다음과 같습니다

답변

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 유형은 System.Object이 아니므로 null을 캐스팅해도 System.Object를 다른 유형으로 변환하는 데 예외가 발생하지 않습니다.

둘째, 객체가 null 인 경우 obj is SomeTypefalse으로 평가됩니다. 따라서 GetObjectToSerialize는 첫 번째 인수가 null이면 null을 반환합니다. GetDataContractType에 대한 호출 사이트를 표시하지 않으므로 null 속성 값이 어떻게 영향을 미치는지 알기가 어렵습니다.

편집 2 : 물론

, 위 때문에 type.GetType() 반환 typeof(System.RuntimeType), not 대해서 typeof (System.Type)`, 올바르지 않습니다.

정확한 분석 결과는 다음과 같습니다.

이것은 일반적인 실수입니다. type 그 자체는 당신의 (아마도 프록시) 객체의 타입을 나타내는 객체입니다; 고정 유형 typeSystem.Type입니다. 런타임 유형은 대체로 System.RuntimeType입니다. 따라서 type.GetType()typeof(System.RuntimeType)이고 그 기본 유형은 System.Reflection.TypeInfo입니다. 따라서 코드는 프록시의 기본 유형이 아닌 System.Reflection.TypeInfo 참조를 직렬화합니다.

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

type.GetType(). BaseType에 문제가없는 것 같습니다. GetDataContractType은 항상 올바른 형식을 반환합니다. (Never System.Object). 귀하의 제안을 시도해보십시오. 그리고 그것은 이미 존재하는 코드와 똑같은 결과를 낳았습니다. 메소드에서 반환 된 값이 정확하기 때문에이 부분이 맞는지 아닌지는 문제가 아닌 것으로 보입니다. System.Object가 아니라 올바른 참조 유형) –

+0

문제가 직렬화 내부에있는 것 같지 않습니다 (솔루션이있을 수 있지만 직렬화 중에 예외가 발생하지 않음) 예외는 DEserializing 중에 클라이언트에 발생합니다. –

+0

기본적으로 클라이언트는 캐스팅 예외를 반환하는 'XXXXXXX'형식으로 직렬화하려고하는 System.Object (NULL 값이어야 함)를 수신합니다. 어쩌면 그것은 실제 XML을 라인을 통해 전송하는 것이 좋겠지 만, XML을 서비스로 보내거나 클라이언트가받는 방법을 모르겠습니다. –

0

에 따르면 : IDataContractSurrogate.GetObjectToSerialize

이 방법 때문에 직렬화 온 null을 반환하지 않아야 GetDataContractType의 끝에서 return 문 바로 앞에이 줄을 추가

봅니다 어떤 일이 볼 수 데이터 이 Object 유형으로 캐스팅되고 InvalidCastException이 발생합니다.

해결책을 찾으려고했지만 지금까지는 할 수 없었습니다.

관련 문제