1

我有一个关于 Msmq 和 linq 实体序列化的问题。

我有一个主消息队列和一个错误消息队列。一个进程使用下面的 Send 方法将项目发送到主队列。第二个进程批量接收来自主队列的项目。第二个进程,在异常情况下,将项目发送到错误队列。在此期间,我得到一个 System.ObjectDisposedException 异常。

我正在使用 LINQ-sql,Item 对象是一个可序列化的实体(DataContext 的序列化模式是单向的)。

在 dbml 中,Item 实体与 Source 实体有关联(参见堆栈跟踪中的 Item.get_Source() 行)。我猜测 ObjectDisposedException 异常是在调用 Item 的 Source 的 getter 时发生的。项目的 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

我怀疑这是因为您使用的是 XmlSerializer,而 LINQ-to-SQL 中的序列化挂钩是为 DataContractSerializer 设计的。不同之处在于后者支持序列化前/后序列化回调,用于帮助禁用级联加载。

我怀疑当序列化程序在模型中爬行时,序列化会导致导航属性延迟加载。使用 DataContractSerializer 可以避免这种情况。或者,考虑在序列化之前完全加载您的模型(至少是可访问的部分)。

如果模型尝试延迟加载,但底层连接/数据上下文不再可用,它将失败。

另一种更好的方法:

不要用延迟加载等序列化复杂模型

相反,IMO 使用你的数据上下文来填充一个愚蠢的、无知的普通 POCO/DTO 模型,它什么都不知道,什么也不做,除了充当一个非常容易序列化的简单数据。这种方法在我的经验中效果更好(当涉及到序列化时,这是不小的经验)。作为一个附带的好处,因为这将是您的POCO/DTO,您可以简单地将其配置为适合您喜欢的任何序列化程序。

于 2012-08-02T21:06:31.170 回答