0

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

我有一个 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
}

我将多线程部分基于此 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 提供者,错误:0 - 由于目标机器主动拒绝,无法建立连接。)

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

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

4

1 回答 1

0

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

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

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

于 2009-12-08T20:48:14.433 回答