0

I am beginning to learn about Async / Task functions for running cancellable SQL queries in VB.NET.

I have the following code in a class library which runs two tasks and does not have any exception handling, as I would like to handle this in the application that calls the class library. I have the following code in the class library.

Public Async Function DirectoryList(ct As CancellationToken) As Task(Of List(Of Directory))
    ct.ThrowIfCancellationRequested()

    Dim ds As DataSet
    Dim dirs As List(Of Directory)
    Dim ctUnregister As CancellationTokenRegistration

    ds = Await Task.Run(Function()
                            Using newConnection = New SqlConnection(Me.InitializationData.ConnectionString)
                                Using sqlAdapter = New SqlDataAdapter("DirectoryList", newConnection)
                                    ctUnregister = ct.Register(Sub() sqlAdapter.SelectCommand.Cancel())

                                    With sqlAdapter
                                        .SelectCommand.CommandType = CommandType.StoredProcedure
                                        .SelectCommand.CommandTimeout = Me.InitializationData.CommandTimeout
                                    End With

                                    Dim newDataSet As DataSet = New DataSet()

                                    sqlAdapter.Fill(newDataSet)
                                    Return newDataSet
                                End Using
                            End Using

                            ' Remove the registration we set earlier so the cancellation token can be used normally.
                            ctUnregister.Dispose()
                        End Function, ct)

    dirs = Await Task.Run(Function()
                              Dim dirsResult As New List(Of Directory)

                              Dim tbl As DataTable = ds.Tables(0)

                              For Each row As DataRow In tbl.Select()
                                  ' process the data

                                  ct.ThrowIfCancellationRequested()
                              Next

                              Return dirsResult
                          End Function, ct)

    Return dirs
End Function

I then call it as follows:

Try
    dirs = Await databaseLibrary.DirectoryList(cts.Token)
    MsgBox("Query complete!")
Catch ex As System.Data.SqlClient.SqlException
    MsgBox("Cancelled (SQL)")
Catch ex2 As OperationCanceledException
    MsgBox("Cancelled")
Catch ex3 As Exception
    MsgBox("Cancelled")
End Try

Functionally it seems to work as expected - I can cancel the requests and exceptions are thrown as expected.

However I would like to handle the exceptions so I can gracefully cancel the tasks, but even when running in Debug mode within the IDE, the app still breaks and the exception (e.g. SqlException) is shown within the IDE. If I step on, eventually the Catch logic runs.

When the application is run outside of the IDE, the exception handling works as expected.

This seems different to the normal behaviour when running in the debugger- normally the exception handling runs but only breaks if the exception is unhandled.

Why is this behaviour different, presumably because of the Async functions?

1
  • Then return a task that you can handle... Commented Mar 29, 2019 at 13:50

1 Answer 1

1

when running in Debug mode within the IDE, the app still breaks and the exception (e.g. SqlException) is shown within the IDE.

Why is this behaviour different, presumably because of the Async functions?

Exceptions thrown by asynchronous functions are not caught directly by the catch. What actually happens is that the exception is caught by a compiler-generated state machine, and that exception is placed on the returned Task. Later, when that task is awaited, the exception is re-raised, and then can be caught by the catch.

However, this "indirection" in exception handling means that the IDE does kind of freak out when the exception is initially thrown. As far as it can tell, there is no catch in your code that is going to catch it, so that's why it breaks and displays the exception. It doesn't know that the compiler-generated code will catch it. You can tell the IDE not to worry about uncaught exceptions.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.