Q:

Why is the default implementation of DbConnection.OpenAsync(CancellationToken) synchronous?

I was reading the documentation for DbConnection.OpenAsync(CancellationToken) and found the following snippet:

The default implementation invokes the synchronous Open call and returns a completed task. The default implementation will return a cancelled task if passed an already cancelled cancellationToken. Exceptions thrown by Open will be communicated via the returned Task Exception property.

Now, if I were on a spotty/slow network connection and used a database provider that hasn’t overridden DbConnection.OpenAsync(CancellationToken) (i.e., I’m using something other than System.Data.SqlClient), and if I plopped that into a UI Button’s event handler, like: (hypothetical code, untested)
async void button1_Clicked(object sender, EventArgs e)
{
    using (var connection = MyProviderFactory.CreateConnection())
    {
        button1.Text = "Opening…";
        connection.ConnectionString = _SomeString;
        try
        {
            await connection.OpenAsync(default);
            button1.Text = "Opened successfully!";
        }
        catch (Exception ex)
        {
            button1.Text = ex.Message;
        }
    }
}

Based on the documentation I quoted, if the connection took long enough to complete, my Form would be “ (Not Responding)” while the connection was being established if the provider hasn’t overridden the default implementation. To prevent this from happening regardless of the underlying database provider, I might as well do await Task.Run(async () => await connection.OpenAsync());. Why is the default implementation this way and how is one supposed to know when Task.Run() is needed without writing provider-aware code?

A:

Your await Task.Run(async () => await connection.OpenAsync()) wouldn't execute connection.OpenAsync() in the same thread, but it's perfectly reasonable for connection.OpenAsync() and connection.Open() to depend on thread-local state. They may and generally should pay attention to Transaction.Current, for instance. If .NET Framework silently executed connection.Open() in a background thread, some people would get very wrong results.

