2017-07-24 71 views
3

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

当这个小应用程序运行时,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(); 

然而,ExecuteScalarAsync仍然没有当我取消CancellationTokenSource取消。有任何想法吗???

+0

这可能会帮助您进入正确的方向https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/asynchronous-programming – Hassan

+0

谢谢哈桑。我已经看过这个页面,但它似乎没有回答我的基本问题。 ExecuteScalarAsync是否真正异步? – dtaylor

+0

的确是它的异步。我在上面提到的链接是帮助你如何处理调用异步方法,使它们保持异步。至于方法本身,哈桑,是的,我现在可以看到它是这样的https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand.executescalarasync?view=netframework-4.7 – Hassan

回答

4

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

您还需要异步打开连接,正如您也发现的那样。该链接包含开放连接,获取数据和异步取消查询的好例子。

Doug编辑**

是的,哈桑是正确的。当问题出现在Open中时,我被挂上了让ExecuteScalarAsync工作。根据经验,在未来的规则我总是会调用命令是这样的:

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

这样一来,如果有连接问题,异步和取消的行为仍然可以工作。

谢谢哈桑。