1

Msmqとlinqエンティティのシリアル化について質問があります。

プライマリメッセージキューとエラーメッセージキューがあります。1つのプロセスは、以下のSendメソッドを使用してアイテムをプライマリキューに送信します。2番目のプロセスは、プライマリキューからアイテムをバッチで受け取ります。2番目のプロセスは、例外として、アイテムをエラーキューに送信します。この間に、System.ObjectDisposedException例外が発生します。

LINQ-sqlを使用していますが、Itemオブジェクトはシリアル化可能なエンティティです(DataContextのシリアル化モードは単方向です)。

dbmlでは、ItemエンティティはSourceエンティティに関連付けられています(スタックトレースのItem.get_Source()行を参照してください)。アイテムのソースのゲッターが呼び出されたときにObjectDisposedException例外が発生すると推測しています。アイテムのSourceIDは、プライマリメッセージキューに送信される前でも入力されます。LINQは、DataContextを使用して遅延読み込みされたソースにアクセスしようとし、ObjectDisposedExceptionをスローするようです。アイテムをプライマリキューに送信する場合とエラーキューに送信する場合の違いがわかりません。

何か案は?

スタックトレース:

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);
    }
}

キューから次のバッチアイテムを取得します。

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

1 に答える 1

4

これは、LINQ-to-SQLのシリアル化フックがDataContractSerializerを対象としている場合に、XmlSerializerを使用しているためと思われます。違いは、後者がカスケードロードを無効にするために使用されるシリアル化前後のコールバックをサポートしていることです。

私の疑いは、シリアライザーがモデルをクロールするときに、シリアル化によってナビゲーションプロパティが遅延ロードされる原因になっていることです。DataContractSerializerを使用すると、これを回避できます。または、シリアル化する前に、モデル(少なくとも到達可能なパーツ)を完全にロードすることを検討してください。

モデルが遅延ロードを試みても、基になる接続/データコンテキストが使用できなくなった場合、モデルは失敗します。

別のはるかに優れたアプローチ:

遅延読み込みなどで複雑なモデルをシリアル化しないでください

代わりに、IMOは、データコンテキストを使用して、シリアル化が非常に簡単な単純なデータとして機能することを除いて、何も知らず、何もしない、無知で無知なプレーンPOCO/DTOモデルにデータを入力します。このアプローチは、私の経験でははるかにうまく機能します(そして、シリアル化に関しては、それは少なからぬ経験です)。副次的な利点として、これがPOCO / DTOになるため、任意のシリアライザーに適したものになるように簡単に構成できます

于 2012-08-02T21:06:31.170 に答える