2012-08-02 3 views
1

linq 엔터티의 Msmq 및 serialization 관련 질문이 있습니다.MSMQ 및 LINQ 엔터티 개체 직렬화 예외

기본 MessageQueue와 오류 MessageQueue가 있습니다. 한 프로세스는 아래 Send 메서드를 사용하여 항목을 Primary 큐로 보냅니다. 두 번째 프로세스는 일차 큐에서 항목을 일괄 적으로받습니다. 두 번째 프로세스 (예외시)는 항목을 오류 대기열로 보냅니다. 이 동안 System.ObjectDisposedException 예외가 발생합니다.

저는 LINQ-sql을 사용하고 Item 객체는 직렬화 가능한 엔티티입니다 (DataContext의 직렬화 모드는 단방향 임).

dbml에서 Item 엔티티는 Source 엔티티에 대한 연결을 갖습니다 (Stacktrace의 Item.get_Source() 행 참조). 항목의 소스 getter가 호출 될 때 ObjectDisposedException 예외가 발생합니다. Item의 SourceID는 Primary MessageQueue로 전송되기 전에 채워집니다. LINQ는 DataContext를 사용하여 지연로드 된 소스에 액세스하려고 시도하고 ObjectDisposedException을 throw합니다. 기본 대기열과 오류 대기열에 항목을 보내는 것의 차이점을 잘 모르겠습니다.

아이디어가 있으십니까?

스택 트레이스 :

System.InvalidOperationException was caught 
    Message=There was an error generating the XML document. 
    Source=System.Xml 
    StackTrace: 
     at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id) 
     at System.Xml.Serialization.XmlSerializer.Serialize(Stream stream, Object o, XmlSerializerNamespaces namespaces) 
     at System.Messaging.XmlMessageFormatter.Write(Message message, Object obj) 
     at System.Messaging.Message.AdjustToSend() 
     at System.Messaging.MessageQueue.SendInternal(Object obj, MessageQueueTransaction internalTransaction, MessageQueueTransactionType transactionType) 
     at namespace.Data.ImportServices.Msmq.MsmqProcessor`1.Send(MessageQueue q, List`1 items) in D:\Workspace\namespace.Data\ImportServices\Msmq\MsmqProcessor.cs:line 95 
    InnerException: System.ObjectDisposedException 
     Message=Cannot access a disposed object. 
Object name: 'DataContext accessed after Dispose.'. 
     Source=System.Data.Linq 
     ObjectName=DataContext accessed after Dispose. 
     StackTrace: 
      at System.Data.Linq.DataContext.CheckDispose() 
      at System.Data.Linq.DataContext.GetTable(Type type) 
      at System.Data.Linq.CommonDataServices.GetDataMemberQuery(MetaDataMember member, Expression[] keyValues) 
      at System.Data.Linq.CommonDataServices.DeferredSourceFactory`1.ExecuteKeyQuery(Object[] keyValues) 
      at System.Data.Linq.CommonDataServices.DeferredSourceFactory`1.Execute(Object instance) 
      at System.Data.Linq.CommonDataServices.DeferredSourceFactory`1.DeferredSource.GetEnumerator() 
      at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source) 
      at System.Data.Linq.EntityRef`1.get_Entity() 
      at namespace.Data.Item.get_Source() in D:\Workspace\namespace.Data\DB.designer.cs:line 4757 
      at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterItemMsmq.Write25_Item(String n, String ns, Item o, Boolean isNullable, Boolean needType) 
      at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterItemMsmq.Write26_ItemMsmq(String n, String ns, ItemMsmq o, Boolean isNullable, Boolean needType) 
      at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterItemMsmq.Write27_ItemMsmq(Object o) 
     InnerException: 

코드 :

가 큐에 항목을 보냅니다.

void Send(MessageQueue q, List<T> items) 
{ 
    using (q) 
    { 
     ((XmlMessageFormatter)q.Formatter).TargetTypes = new Type[] { typeof(T) }; 
     foreach (var item in items) 
      q.Send(item); // <-- ex occurs while sending to Error message queue 
    } 
} 

콜백을 사용하여 대기열 및 프로세스 항목에서 항목을받습니다. 예외가 발생하면 항목을 오류 대기열로 보냅니다.

void Receive(MessageQueue q, Action<List<T>> processCallback) 
{ 
    List<T> items = null; 
    try 
    { 
     items = GetNextBatchItems(q); 
     processCallback(items); 
    } 
    catch (Exception ex) 
    { 
     // sent error messages to the Error queue 
     var errorQ = _queueFactory.GetErrorQueue(q); 
     Send(errorQ, items); 
    } 
} 

다음 배치 항목을 대기열에서 가져옵니다. 의 직렬화 후크가 LINQ - 투 - SQL은 DataContractSerializer를위한 것입니다 때, XmlSerializer를를 사용하고 있기 때문에 나는이 의심

List<T> GetNextBatchItems(MessageQueue q) 
{ 
    var items = new List<T>(); 
    var batchCount = _queueFactory.GetBatchCount(q); 
    ((XmlMessageFormatter)q.Formatter).TargetTypes = new Type[] { typeof(T) }; 
    while (items.Count < batchCount) 
    { 
     var message = q.Receive(); 
     if (message.Body is T) 
      items.Add((T)message.Body); 
    } 
    return items; 
} 

답변

4

이다. 차이점은 캐스케이드 로딩을 ​​비활성화하는 데 사용되는 직렬화 전/후 콜백을 지원한다는 것입니다.

내 생각에 직렬 변환기가 모델을 통해 크롤링 할 때 탐색 속성이 지연로드되는 원인이 될 수 있습니다. DataContractSerializer를 사용하면이 문제를 방지 할 수 있습니다. 또는 직렬화하기 전에 모델 (적어도 도달 가능한 부품)을 완전히로드하는 것을 고려하십시오.

모델이 느슨하게로드하려고하지만 기본 연결/데이터 컨텍스트를 더 이상 사용할 수 없으면 실패합니다.

또 다른, 훨씬 더 나은 방법 :

IMO, 바보, 무지 일반 POCO/DTO 모델을 채우는 데이터 컨텍스트를 사용

대신 지연로드 등 복잡한 모델을 직렬화하지 않습니다 , 그것은 아무 것도 모르고 아무것도하지 않습니다. 단, 직렬화가 정말 쉬운 간단한 데이터로 작동하십시오. 이 접근법은 제 경험에서 훨씬 잘 작동합니다 (그리고 직렬화에 관해서는 경험이 적지 않습니다). 부작용으로는 POCO/DTO가되므로 원하는 직렬 변환기에 적합하도록 쉽게 구성 할 수 있습니다.

+0

나는 100 % 동의하여 직렬화 된 모델을 최소화하고, 바람직하게는 DB 엔티티를 전혀 사용하지 않습니다. 이를 통해 직렬화에 대해 (DB 모델을 오염시키지 않으면 서) 이해할 수있는 방식으로 주석을 달고 간단하게 유지할 수 있습니다. –

+0

DataContractSerializer를 사용하여 예외가 없으므로이 문제를 해결할 것입니다. 완전로드는 옵션이지만 원하는 것은 아닙니다. 무거운 LINQ 엔터티 개체 대신 POCO 더미 개체를 사용하는 장점을 알고있었습니다. 그러나 나는 이것을 시험해보고 싶었다. – hIpPy