SqlClient.SqlCommand.ExecuteScalarAsync的行为类似于同步调用

时间:2017-07-24 17:27:42

标签: c# asynchronous async-await sqlcommand

我已将我的应用程序剥离到最小的POC,我仍然得到相同的效果。似乎ExecuteScalarAsync的行为类似于同步调用。我认为当遇到await时,异步方法中的其余代码暂停,消息泵返回并从消息队列中获取另一条消息,允许UI继续。标量调用完成后,异步方法的其余部分将被放回消息队列中,以便完成。

当这个小应用程序运行时,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仍然没有取消。任何想法???

1 个答案:

答案 0 :(得分:4)

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

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

来自道格的编辑**

是的,哈桑是对的。当问题出在Open上时,我很想让ExecuteScalarAsync工作。作为未来的经验法则,我总是会调用这样的命令:

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

这样,如果存在连接问题,异步和取消行为仍然有效。

谢谢哈桑。