Skip to content

DbCommand.ExecuteReaderAsync throws TaskCanceledException with wrong CancellationToken #1250

Open
@Coder3333

Description

I am witnessing this behavior in packages\netstandard.library\2.0.3\build\netstandard2.0\ref\netstandard.dll when running from a .Net Core 3.1 application.

When I call DbCommand.ExecuteReaderAsync with a CancellationToken and cancel it before it finishes, an exception is thrown that contains a TaskCanceledException buried down in the InnerExceptions. I expect that behavior, but the issue is that the TaskCanceledException.CancellationToken is not the CancellationToken that I provided to ExecuteReaderAsync. I do not know where this other CancellationToken is coming from, but I thought we were only supposed to suppress TaskCanceledException in our applications if it is for the provided CancellationToken, so I end up with a lot more unhandled exceptions than I want.

Is it safe to suppress any TaskCanceledException, no matter what CancellationToken it contains? Where does this mystery CancellationToken come from, and could ExecuteReaderAsync just return the CancellationToken given to it?

                using (var cts = new CancellationTokenSource())
                {
                    using (var cmd = db.GetSqlStringCommand(queryString))
                    {
                        cts.Cancel();
                        try
                        {
                            using (var rdr = cmd.ExecuteReaderAsync(cts.Token).Result)
                            {
                            }
                        }
                        catch (Exception ex3)
                        {
//  if you inspect ex3, you will find a TaskCanceledException buried in InnerExceptions, but its CancellationToken is not cts.Token
                            throw;
                        }
                    }
                }

Additional Comments:
I have tested this with "System.Data.SqlClient.SqlCommand" and "Microsoft.Data.SqlClient.SqlCommand". The result is the same in both cases where the TaskCanceledException holds a new CancellationToken and the TaskCanceledException is wrapped in an AggregateException, so I have to dig down in the exception stack to find the TaskCanceledException , which means I cannot just catch TaskCanceledException.

System.AggregateException: One or more errors occurred. (A task was canceled.)
---> System.Threading.Tasks.TaskCanceledException: A task was canceled.
--- End of inner exception stack trace ---
at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task1.GetResultCore(Boolean waitCompletionNotification) at System.Threading.Tasks.Task1.get_Result()

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions