2

I was wondering why the exception in this Click event is not being caught? Nothing fancy in the code, except for statusLabel displaying the status of the process to the user. myDataTable is a local variable; the goal is to assign the result to it.

Does GetDataTable have to be asynchronous as well?

public DataTable GetDataTable(string connectionString, string cmdText)
{
    DataTable dt = new DataTable();
    using (SqlConnection conn = new SqlConnection(connectionString)) {
        using (SqlCommand comm = new SqlCommand(cmdText, conn)) {
            conn.Open();
            dt.Load(comm.ExecuteReader);
            return dt;
        }
    }
}

private async void Button1_Click(object sender, EventArgs e)
{
    try {
        statusLabel.Text = "Processing...";

        Task<DataTable> dtTask = Task.Run(() => GetDataTable(connectionString, commandText));
        await dtTask;
        myDataTable = dtTask.Result;

        statusLabel.Text = "Done!";
    } catch (Exception ex) {
        MessageBox.Show("Error");
    }
}

UPDATE

I managed to do solve this problem by making GetDataTable() return a Task of DataTable and changing both .Open and .ExecuteReader to their asynchronous counterparts. For the other method, those three lines inside the Try block I reduced to one:

myDataTable = await GetDataTable(connectionString, commandText);

Thanks to everyone's patiences!

svick
  • 236,525
  • 50
  • 385
  • 514
AwonDanag
  • 329
  • 1
  • 11
  • 3
    You are using the Result property .. might as well not use a Task .. – G-Man Jan 13 '17 at 03:46
  • I'm assuming the problem is using await on a non async method. I can analyse exactly what is going on (hence a comment and not an answer). You can remove that line completely or use GetAwaiter().GetResult() instead of that line and the following one. See http://stackoverflow.com/questions/16877262/how-can-i-await-an-async-method-without-an-async-modifier-in-this-parent-method and http://stackoverflow.com/questions/17284517/is-task-result-the-same-as-getawaiter-getresult – Eli Algranti Jan 13 '17 at 03:48
  • Or as G-Man says don't even use a task if you are waiting synchronously for the result. – Eli Algranti Jan 13 '17 at 03:49
  • also instead of using ExecuteReader, if you are looking for an async operation, use SqlCommand.ExecuteReaderAsync https://msdn.microsoft.com/en-us/library/hh204836(v=vs.110).aspx – G-Man Jan 13 '17 at 03:49
  • I'm trying to make it asynchronous, because `GetDataTable()` can take very long and block the UI. I'm trying to avoid this. :( – AwonDanag Jan 13 '17 at 03:51
  • 3
    @G-Man, he's awaiting `dtTask` before assigning `myDataTable = dtTask.Result`, though. So it's still async. Essentially the same thing as `myDataTable = await dtTask`. – Ilian Jan 13 '17 at 03:56
  • @IIian not pretty.. – G-Man Jan 13 '17 at 04:01
  • I know. Just commenting that it's not synchronously waiting for the result (as others have commented within the thread). – Ilian Jan 13 '17 at 04:07
  • Setting the await-async bit for a moment, how can I make the code enter the catch block when the exception occurs at the `GetDataTable()` side? (e.g. connection login failed, database/table does not exist) Thanks! :) – AwonDanag Jan 13 '17 at 04:10
  • 1
    @AwonDanag I can't reproduce your problem in my test code. Are you certain that `GetDataTable()` is throwing an exception or that you're not swallowing it? What happens if, for testing purposes, you replace the contents of `GetDataTable()` to just throw an exception? – Ilian Jan 13 '17 at 04:15
  • @IlianPinzon If I did it right, it just says 'An exception of type 'System.Exception' occurred in Class1.dll but was not handled in user code'. I should add that `GetDataTable()` is just referenced from a separate project, and is actually `Public Shared`. – AwonDanag Jan 13 '17 at 05:15
  • @AwonDanag: When posting a question about an exception, please include the exception type, message, and stack trace in the question itself. – Stephen Cleary Jan 13 '17 at 14:02

2 Answers2

1

Instead of using Task.Run to convert a synchronous method to an asynchronous one, use ExecuteReaderAsync to truly load data asynchronously. After that, your code becomes a lot simpler:

public async Task<DataTable> GetDataTable(string connectionString, string cmdText)
{
    DataTable dt = new DataTable();
    using (SqlConnection conn = new SqlConnection(connectionString)) {
        using (SqlCommand comm = new SqlCommand(cmdText, conn)) {
            conn.Open();
            var reader=await comm.ExecuteReaderAsync();
            dt.Load(reader);
            return dt;
        }
    }
}

private async void Button1_Click(object sender, EventArgs e)
{
    try {
        statusLabel.Text = "Processing...";

        myDataTable = await GetDataTable(connectionString, commandText);

        statusLabel.Text = "Done!";
    } catch (Exception ex) {
        MessageBox.Show("Error");
    }
}

GetDataTable became an asynchronous method that executes the query asynchronously and returns a reader with:

var reader=await comm.ExecuteReaderAsync();
dt.Load(reader);
return dt;

After that, setting the myDataTable variable only requires using an await:

myDataTable = await GetDataTable(connectionString, commandText);
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • Cool! As I said in my edit, I used the async counterparts of both `Open()` and `ExecuteReader()` and it seems to work out fine... I see you have used only `ExecuteReaderAsync()` - will there be a difference? – AwonDanag Jan 17 '17 at 09:24
0

I believe the answer you are looking for is here: Async void exception handling

I'm assuming that the 'Button1_Click' method was meant to be 'async void'?:

private async void Button1_Click(object sender, EventArgs e)
{
    ...
}
Community
  • 1
  • 1
karmasponge
  • 1,169
  • 2
  • 12
  • 26