This might be a bit complicated, but bear with me.
I have a Windows Forms app. It uses strongly typed DataSets via the XSD designer. I am running data access queries via an asynchronous thread, performed like so:
// 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
}
I've based the multi-threading part on the suggestions in this SO question. It works fine... except when there are DB errors (in the form of SqlExceptions) when calling GetList() on the TableAdapter.
If I deliberately corrupt the connection string, which causes the connection attempts to fail, a series of about 10 SqlExceptions are generated somewhere in .NET framework code land and caught (first-chance exceptions). After that, the application throws an unhandled exception and terminates the program. I know this because I have a last-chance exception catcher on the Main method that logs them should something bubble up all the way. But my try-catch in the worker and callback functions, as detailed above, are never triggered. It seems like they are by-passed completely? Even though the line that starts the problems is inside a try-catch block.
It is an SqlException, so it should be caught, but instead the try-catch on the Main method catches a TargetInvocationException (with an SqlException as the InnerException). The error is as expected:
A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: TCP Provider, error: 0 - No connection could be made because the target machine actively refused it.)
But why is my try-catch missing it and I'm getting a TargetInvocationException instead? What gives? I get that this is because the exception happens on a separate thread from the main UI thread, but if the exception generated is generated in this separate thread in code that I have a try-catch with, why is it ignoring it and bailing on the thread entirely instead and then causing the main thread to panic and abort?
Ideally, the catch (SqlException) above should catch it, and I can notify via the UI as well as enable an Offline mode in case there are DB problems. The details don't matter for this question, suffice to say that I can handle it fully there and thus stop the exception in its tracks... if only my try-catch would actually catch it!