【问题标题】:How to catch connection timeout exceptions from XSD-generated typed DataSets?如何从 XSD 生成的类型化数据集中捕获连接超时异常?
【发布时间】:2010-12-24 13:58:43
【问题描述】:

这可能有点复杂,但请耐心等待。

我有一个 Windows 窗体应用程序。它通过 XSD 设计器使用强类型数据集。我正在通过异步线程运行数据访问查询,执行如下:

// Calling it in code on the main thread:
LoadDataList_WorkerCaller dataDelegate = new LoadDataList_WorkerCaller(LoadDataList_Worker);
IAsyncResult iar = default(IAsyncResult);
iar = dataDelegate.BeginInvoke(LoadDataList_Complete, null);

// How they are defined in the class:
private delegate TypedDataSets.DataListDataTable LoadDataList_WorkerCaller();
private TypedDataSets.DataListDataTable LoadDataList_Worker()
{
    // ...blah blah
    try
    {
        DataListTableAdapter adapter = new DataListTableAdapter();
        TypedDataSets.DataListDataTable dataList = adapter.GetList(); // causes connection attempt
    }
    catch (SqlException sqlex)
    {
        // Log DB error and notify user of problem
    }

    return dataList;
}
private delegate void LoadDataList_CompleteCaller(IAsyncResult iar);
private void LoadDataList_Complete(IAsyncResult iar)
{
    if (InvokeRequired)
    {
        LoadDataList_CompleteCaller invokeDelegate = new LoadDataList_CompleteCaller(LoadDataList_Complete);
        Invoke(invokeDelegate, new object[] { iar });
        return;
    }

    // Downcast the IAsyncResult to an AsyncResult -- it's safe and provides extra methods
    System.Runtime.Remoting.Messaging.AsyncResult ar = (System.Runtime.Remoting.Messaging.AsyncResult)iar;

    LoadDataList_WorkerCaller dataDelegate = (LoadDataList_WorkerCaller)ar.AsyncDelegate;
    TypedDataSets.DataListDataTable dataList = null;

    try
    {
        dataList = dataDelegate.EndInvoke(iar);
    }
    catch (Exception ex)
    {
        // Final fail-safe, for non-DB exceptions; we'll log 'em and such here
    }

    // ... use dataList object as normal
}

我的多线程部分基于this SO question 中的建议。它工作正常...除非在 TableAdapter 上调用 GetList() 时出现 DB 错误(以 SqlExceptions 的形式)。

如果我故意破坏连接字符串,从而导致连接尝试失败,则会在 .NET 框架代码区域的某处生成一系列大约 10 个 SqlException 并被捕获(第一次机会异常)。之后,应用程序抛出一个未处理的异常并终止程序。我知道这一点,因为我在 Main 方法上有一个最后机会异常捕获器,它记录它们应该一直冒泡。但是如上所述,我在 worker 和回调函数中的 try-catch 永远不会被触发。似乎它们完全被绕过了?即使开始问题的行位于 try-catch 块内。

它是一个 SqlException,所以它应该被捕获,但是 Main 方法上的 try-catch 会捕获一个 TargetInvocationException(SqlException 作为 InnerException)。错误如预期:

与 SQL Server 建立连接时出现与网络相关或特定于实例的错误。服务器未找到或无法访问。验证实例名称是否正确以及 SQL Server 是否配置为允许远程连接。 (提供者:TCP Provider,错误:0 - 由于目标机器主动拒绝,无法建立连接。)

但是为什么我的 try-catch 丢失了它,而我却得到了 TargetInvocationException 呢?是什么赋予了?我知道这是因为异常发生在与主 UI 线程不同的线程上,但是如果生成的异常是在我有 try-catch 的代码中的这个单独线程中生成的,为什么它会忽略它并放弃完全改为线程,然后导致主线程恐慌并中止?

理想情况下,上面的捕获 (SqlException) 应该捕获它,并且我可以通过 UI 通知以及启用离线模式以防数据库问题。这个问题的细节并不重要,我只想说我可以在那里完全处理它,从而阻止异常发生......如果只有我的 try-catch 真的能抓住它!

【问题讨论】:

    标签: winforms exception-handling multithreading try-catch invoke


    【解决方案1】:

    在我看来,问题出在您发布的代码之外的某个地方。如果我们假设有问题的数据集正在使用 SqlClient,并且您尝试捕获的异常的完全限定类型是 System.Data.SqlClient.SqlException,那么代码应该完全按照您的预期工作。该问题的一种可能解释是类型化数据集使用 System.Data.OleDb 而不是 System.Data.SqlClient。

    在继续之前,我建议您添加另一个捕获异常而不是 SqlException 的 catch 块。如果在这种情况下您仍然无法捕获异常,那么一定是您的代码结构不是您所期望的,并且异常来自其他地方。顺便说一句,最好始终使用通用 catch 块包装工作线程委托代码以避免您遇到的问题。

    另一个好的完整性检查是完全限定 SqlException (System.Data SqlClient.SqlException) 的类型。有可能在您的代码库中的某个地方,有人声明了另一个名为 SqlException 的类,它隐藏了真正的 McCoy。

    【讨论】:

    • 它肯定是使用SqlClient 和所有,并且异常类型是正确的(正如我通过TargetInvocationException 的InnerException 确认的那样)。我已将其更改为捕获(异常)并且它的行为没有任何不同。我会尝试放置更多的try-catch,直到最终捕捉到一些东西。感谢您至少考虑一下!
    • 我怀疑异常没有在你认为的地方抛出。如果您还没有这样做,我建议您转储内部异常的完整堆栈帧。
    猜你喜欢
    • 2013-11-24
    • 1970-01-01
    • 2011-11-02
    • 2010-09-06
    • 1970-01-01
    • 1970-01-01
    • 2014-07-09
    • 2011-08-08
    • 2012-03-06
    相关资源
    最近更新 更多