SqlClient.SqlCommand.ExecuteScalarAsync 表现得像同步调用
SqlClient.SqlCommand.ExecuteScalarAsync behaving like synchronous call
我已将我的应用程序精简到最小的 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 仍然没有取消。有什么想法吗???
ExecuteScalarAsync is indeed an async method but your UI halts because you are not calling the methods asynchronously. You can see how you can better handle asynchronous method calls on this Microsoft page.
正如您也发现的那样,您还需要异步打开连接。 link 包含打开连接、获取数据和异步取消查询的好例子。
编辑 ** 来自 Doug
是的,哈桑是对的。当问题一直出在 Open 上时,我对让 ExecuteScalarAsync 工作挂断了。根据经验,以后我总是会这样调用命令:
await connection.OpenAsync(_token);
_command = new SqlCommand(query, connection);
await _command.ExecuteScalarAsync(_token);
这样,如果出现连接问题,异步和取消行为仍然有效。
谢谢哈桑。
我已将我的应用程序精简到最小的 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 仍然没有取消。有什么想法吗???
ExecuteScalarAsync is indeed an async method but your UI halts because you are not calling the methods asynchronously. You can see how you can better handle asynchronous method calls on this Microsoft page.
正如您也发现的那样,您还需要异步打开连接。 link 包含打开连接、获取数据和异步取消查询的好例子。
编辑 ** 来自 Doug
是的,哈桑是对的。当问题一直出在 Open 上时,我对让 ExecuteScalarAsync 工作挂断了。根据经验,以后我总是会这样调用命令:
await connection.OpenAsync(_token);
_command = new SqlCommand(query, connection);
await _command.ExecuteScalarAsync(_token);
这样,如果出现连接问题,异步和取消行为仍然有效。
谢谢哈桑。