【问题标题】:SqlClient.SqlCommand.ExecuteScalarAsync behaving like synchronous callSqlClient.SqlCommand.ExecuteScalarAsync 表现得像同步调用
【发布时间】:2017-07-24 17:27:42
【问题描述】:

我已将我的应用程序简化为最小的 POC,但仍能获得相同的效果。看起来 ExecuteScalarAsync 的行为类似于同步调用。我认为当遇到等待时,异步方法中的其余代码会暂停,消息泵会返回并从消息队列中获取另一条消息,从而允许 UI 继续。当 Scalar 调用完成时,async 方法的其余部分将放回消息队列中,以便完成。

当这个小应用程序运行时,TestConnectionAsync 方法会挂起 UI,并且在 ExecuteScalarAsync 调用超时之前不会执行其他消息。

是我做错了什么,还是这个异步方法的行为类似于同步方法?

表单有两个按钮。第一个运行异步方法,第二个尝试使用令牌取消异步方法。我从来没有机会点击第二个按钮。

Form1.cs

public partial class Form1 : Form
{
    private DB _db = new DB();
    private string _nl = Environment.NewLine;

    public Form1()
    {
        InitializeComponent();
    }

    private async void button1_Click(object sender, EventArgs e)
    {
        textBox1.Text = "Starting" + _nl;
        string resultString
            = (string) await _db.TestConnectionAsync();
        textBox1.AppendText(resultString + _nl);
        textBox1.AppendText("Done" + _nl);
    }

    private void button2_Click(object sender, EventArgs e)
    {
        textBox1.AppendText("Cancelling..." + _nl);
        _db.CancelTest();
        textBox1.AppendText("Submitted Cancel Token" + _nl);
    }
}

DB.cs

public class DB
{
    private SqlCommand _command = null;
    private CancellationTokenSource _tokenSource
        = new CancellationTokenSource();
    private CancellationToken _token;

    public async Task<string> TestConnectionAsync()
    {
        _token = _tokenSource.Token;
        string query = "SELECT COUNT(*) FROM tblDintData";

        try
        {
            using (SqlConnection connection
                 = new SqlConnection(BuildConnectionString()))
            {
                connection.Open();
                _command = new SqlCommand(query, connection);
                await _command.ExecuteScalarAsync(_token);

                return "Successful Database Connection";
            }
        }
        catch (Exception ex)
        {
            return "Connection Failed:"
                + Environment.NewLine + ex.Message;
        }
    }

    public void CancelTest()
    {
        _tokenSource.Cancel();
    }

    private string BuildConnectionString()
    {
        string ret = "";

            ret = "Server=NotARealServer;"
                + "Database=NoSuchDatabase;"
                + "Trusted_Connection=True;";

        return ret;
    }
}

编辑***

好的,我只是通过反复试验发现了一些东西。如果我还通过调用 Connection.OpenAsync 来使 Connection.Open 异步,那么 UI 会突然变得响应。这不直观,但这是我更改的行:

来自:

                connection.Open();

到:

                await connection.OpenAsync();

但是,当我取消 CancellationTokenSource 时,ExecuteScalarAsync 仍未取消。有什么想法???

【问题讨论】:

  • 这可能会帮助您朝着正确的方向前进docs.microsoft.com/en-us/dotnet/framework/data/adonet/…
  • 谢谢哈桑。我看过这个页面,但它似乎没有回答我的基本问题。 ExecuteScalarAsync 真的是异步的吗?
  • 确实是异步的。我上面提到的链接是为了帮助您如何处理调用 Async 方法以使它们保持异步。至于方法本身,docs.microsoft.com/en-us/dotnet/api/…
  • Hassan,是的,我现在可以看到它确实是异步的。对我来说,您还必须通过等待异步打开连接才能使命令正常工作,这并不直观。现在我遇到了取消被忽略的问题。
  • Hassan,当我将令牌放入 OpenAsync 和 ExecuteScalarAsync 时,它开始完美运行。这是有道理的,因为超时的是 Open,而不是 ExecuteScalar。把它放在一个答案中,我会给你功劳。

标签: c# asynchronous async-await sqlcommand


【解决方案1】:

ExecuteScalarAsync 确实是一个异步方法,但是您的 UI 会停止,因为您没有异步调用这些方法。您可以了解如何更好地处理 this Microsoft page 上的异步方法调用。

您还发现了,您还需要异步打开连接。该链接包含打开连接、获取数据和异步取消查询的良好示例。

从 Doug 编辑**

是的,哈桑是对的。当问题一直出在 Open 上时,我一直很想让 ExecuteScalarAsync 工作。根据经验,将来我将始终调用这样的命令:

                await connection.OpenAsync(_token);
                _command = new SqlCommand(query, connection);
                await _command.ExecuteScalarAsync(_token);

这样,如果出现连接问题,异步和取消的行为仍然会起作用。

谢谢哈桑。

【讨论】:

    猜你喜欢
    • 2016-11-09
    • 2021-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多