1

我正在尝试处理通过我创建的 2 个 dll 传递的错误。所以 Console.exe 调用 dll 1。dll 1 完成异步 MQ 消息读取,处理程序调用 dll 2。如果 dll 2 出错,它会毫无问题地传递异常(抛出)。但是 dll 1(异步)处理程序从 dll 2 捕获抛出并给我一个未处理的用户消息。我已经按照 msdn 代码添加到 IAsyncResult 以保持处理程序活动但问题仍然存在。

谁能建议我应该如何处理这个堆栈并将处理程序错误返回到 console.exe 程序,以便我可以将它呈现给用户。下面的代码: -

Console.exe(片段)

try
{
    _msmq.MSMQ_GetMessage(_msgPath);

     //set up the print of the number of queue messages
     Console.WriteLine("Main thread: starting a timer");
     Timer t = new Timer(ComputeBoundOp, _msgPath, 0, 2000);

     Console.Write("Press any key to continue . . .");
     Console.ReadKey(true);

     t.Dispose(); // Cancel the timer now

 }
 catch (MessageQueueException _msgQex)
 {
     Console.WriteLine("An error occurred with the queue:- " + _msgQex);
 }
 catch (Exception _ex)
 {
     Console.WriteLine("An error occurred with the queue:- " + _ex);
 }

DLL 1

public void MSMQ_GetMessage(string _MQ_Path)
{
    try
    {
        //set the correct message queue
        MessageQueue _msgQ = new MessageQueue(_MQ_Path, QueueAccessMode.ReceiveAndAdmin);
        //set the format of the message queue
        _msgQ.Formatter = new XmlMessageFormatter(new Type[] { typeof(_TwitterStreamFeed) });
        _msgQ.ReceiveCompleted += new ReceiveCompletedEventHandler(_msgQ_RecieveCompleted);
        IAsyncResult _result = _msgQ.BeginReceive();
        _asyncList.Add(_result); // asyncList is a global variable of type System.Collections - > this allows the callback to remain open and therefore nit garbage collected while the async thread runs off on it's own
    }
    catch (Exception _ex)
    {
        throw new Exception("_msgQ_get Message threw the following error :- " + _ex);
    }
}

//method to process message
public void _msgQ_RecieveCompleted(object sender, ReceiveCompletedEventArgs e)
{
    try
    {
        //queue that have received a message
        MessageQueue _mq = (MessageQueue)sender;

        //get the messge off the queue
        Message _mqmsg = _mq.EndReceive(e.AsyncResult);

        //set the values back into a formatted struct 
        //now process your SQL....
        Azure_SQL _azuresql = new Azure_SQL();
        _azuresql.writeMessageToStorage((_TwitterStreamFeed)_mqmsg.Body);

        //refresh queue just in case any changes occurred (optional)
        _mq.Refresh();
        //tell MessageQueue to receive next message when it arrives
        _mq.BeginReceive();
    }
    catch (Exception _ex)
    {
        throw;
    }

DLL 2

public void writeMessageToStorage(_TwitterStreamFeed _msmq_message_as_TSF)
{
    try
    {        
        // now do something with the class - i..e write the values to the database
        SqlConnection _azurecon = new SqlConnection(_AzuzeSQLConnection);
        SqlCommand _sqlcmd = new SqlCommand();

        //Setup the command string to call the stored procedure
        //Add the parameter to the parameters collection of the command

        blah blah blah.........  Do SQL writing to Db

        _azurecon.Open();
        SqlDataReader _sqldr_tweet_place = _sqlcmd_place.ExecuteReader(CommandBehavior.CloseConnection);
    }

    //now close things off
    _azurecon.Close();

    }
    catch (Exception _ex)
    {
        // Throw the error to preserve the original
        throw;
    }
4

2 回答 2

0

这样做的原因是,在内部,MessageQueue该类明确地吞下了异常。MessageQueue类引发ReceiveCompleted事件的地方,它在一个try-catch语句内 - 并且catch块是空的。可以这么说,如果您的ReceiveCompleted事件处理程序内部发生异常,则_msgQ_RecieveCompleted()永远不会知道它发生了。

我看到了几个选项,按优先顺序排列。

选项 1 - 转移异步调用的位置

由于这种异常吞咽行为仅在使用BeginReceive(), in时发生MSMQ_GetMessage(),因此您可以从 using 切换BeginReceive()到 just Receive()。然后,进行MSMQ_GetMessage()异步调用,任何抛出的异常都将按预期传播。

附带说明一下,可以使用一种新的(er)替代方法来进行异步调用;Task<>班级。与Thread类相反,Task<>它具有内置的异常处理功能。但是,它确实需要 Framework 4 或更高版本。这里的答案中描述了它的用途有一个很好的解释。

选项 2 - 使用自定义事件

如果无法重构异步调用,您可以在“dll 2”中的类中创建自定义事件,并在“Console.exe”中订阅该事件。因此,当 中发生异常时_msgQ_RecieveCompleted(),您可以引发事件并通知“Console.exe”。

于 2013-07-14T05:10:37.000 回答
0

MessageQueue.BeginReceive() 方法使用标准的 .NET APM(异步编程模型)模式。了解如何正确处理异常非常重要。请务必阅读MSDN 文章,还有许多其他可用的可搜索资源。

在 APM 中,告诉您收到消息的回调在线程池线程上执行。这是让代码快速运行的一种非常有效的方法。然而,当出现问题时,这也是一种非常麻烦的方式。EndReceive() 方法调用可能会抛出异常,它会告诉您接收操作无法完成。它会抛出的标准异常是 ObjectDisposedException。当 MessageQueue 对象被释放时会发生这种情况。在您的程序终止时。您需要捕获该异常并从您的事件处理程序中退出,这是一个预期的异常,并表明自从队列关闭以来,接下来不会发生任何更有用的事情。

然后,消息队列管道中的重大事故可能会引发大量可能的异常。加上你对消息所做的任何事情。看起来你执行了一些 Azure 代码,有很多方法可能会失败。如果你让这样的异常从回调方法中逃脱,就像你做的那样,那么调用堆栈中的任何地方都没有catch子句来处理异常。.NET 处理未处理异常的标准方式是引发 AppDomain.UnhandledException 事件并终止您的程序。如果您实际上没有实现该事件,那么就没有什么可以诊断程序结束的原因,Windows 错误报告对话框没有很好的诊断。

您是否应该尝试处理异常并防止程序终止取决于您。但它非常适合“不要射信使”模式,当引发此类异常时,您的程序不太可能有意义地继续执行。解决问题总是需要人来解决,例如恢复网络连接或修复消息队列。如果您确实抓住了它,那么很有可能一遍又一遍地引发相同的异常。毕竟,您无法在代码中做任何体面的事情来修复网络。

所以这里最好的指导是不要尝试,只要确保 IT 人员有良好的诊断,以便他们可以修复问题。 执行AppDomain.UnhandledException 并显示和记录 e.UnhandledException.ToString() 值。这也将让您了解您的程序可能失败的方式数量。可能有一些常见的情况足以保证捕获,例如临时网络中断。那时,您还将知道如何处理它,换句话说,在 catch 子句中编写什么样的代码。你不可能知道现在要写什么,因此你不应该尝试。

最后但并非最不重要的一点是,请注意您陷入了这个泡菜,因为您不必要地使用了 BeginReceive()。你已经有了一个非常好的线程来做工作。但它没有做任何有用的事情,它卡在 Console.ReadKey() 方法中。特别是在 .NET 4.5 中,这是一个非常棘手的调用方法,它可以防止其他线程向控制台写入任何内容。因此,您的错误报告将不起作用,当它尝试使用 Console.WriteLine() 编写诊断时,它会死锁。

您不妨改用 MessageQueue.Read() 。现在处理异常要容易得多,因为它们发生在同一个线程上。MessageQueue.SynchronizingObject 也有助于在主线程上发生完成回调,但这仅适用于 GUI 应用程序,不适用于控制台应用程序。

于 2013-07-14T12:07:56.367 回答