通过异步dll堆栈处理错误

时间:2013-07-05 09:04:43

标签: c# asynchronous exception-handling msmq

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

任何人都可以建议我应该如何处理这个堆栈并将处理程序错误返回到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;
    }

2 个答案:

答案 0 :(得分:0)

这样做的原因是,在内部,MessageQueue类明确地吞噬了异常。 MessageQueue类引发ReceiveCompleted事件的位置,它位于try-catch语句中 - catch块为空。我只想说,如果您的ReceiveCompleted事件处理程序_msgQ_RecieveCompleted()中发生异常,则不会发生任何事情。

我按优先顺序看了几个选项。

选项1 - 进行异步调用的转移

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

作为旁注,可以使用新的(呃)替代方法进行异步调用; Task<>课程。Thread。与Task<>类相反,_msgQ_RecieveCompleted()内置了异常处理功能。但是,它确实需要Framework 4或更高版本。在答案here中描述了它的用法的很好的解释。

选项2 - 使用自定义事件

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

答案 1 :(得分:0)

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

在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应用程序中运行,而不能在控制台应用程序中运行。