我尝试熟悉async / await。因此,我尝试编写一个C#/ WPF程序,以异步方式查询数据库,而不会阻止我的GUI。
我创建了一个实现INotifyPropertyChanged
接口的对象。此对象提供DataTable
属性,这应该由我的异步函数更改。我的GUI组件绑定到DataTable
属性。
我的对象看起来像这样:
public class AsyncDataDemo : INotifyPropertyChanged
{
protected DataTable data = new DataTable();
public DataTable Data
{
get { return data; }
protected set
{
data = value;
doPropertyChanged("Data");
}
}
protected virtual void doPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChangedEventArgs Arguments = new PropertyChangedEventArgs(propertyName);
PropertyChanged(this, Arguments);
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected async Task<DataTable> OpenQueryAsync(string ConnectionString, string Query)
{
OdbcConnection connection = new OdbcConnection(ConnectionString);
await connection.OpenAsync().ConfigureAwait(false);
OdbcCommand command = new OdbcCommand(Query, connection);
DbDataReader dataReader = await command.ExecuteReaderAsync().ConfigureAwait(false);
DataTable resultData = new DataTable();
resultData.Load(dataReader);
connection.Close();
return resultData;
}
public async void RunQueryAsync(string Query)
{
Data = await OpenQueryAsync("<ConectionString>", (Query as string)).ConfigureAwait(false);
}
}
在我点击的按钮点击事件中:
private void Button_Click(object sender, RoutedEventArgs e)
{
data.RunQueryAsync("SELECT * FROM BigTable");
}
这样可以正常工作,但有一个例外:按钮单击会阻止我的GUI直到加载数据并且我不明白为什么。
有人可以向我解释我的失败吗?我不明白为什么异步函数不会异步运行?
此致
答案 0 :(得分:5)
你真的需要通过一个调试器来确定哪个步骤需要很长时间。最可能的候选者是resultData.Load(dataReader);
,如果您调用的每个函数都返回已经有.IsCompleted == true
的任务,那么代码仍然可以在UI线程上的方式。
如果任务已经完成,并且您await
即使您执行了ConfigureAwait(false)
,那么您也会继续使用UI线程。所有需要发生的事情都是OpenAsync()
和ExecuteReaderAsync()
需要非常快速地完成或同步完成(两者都很可能)。
您可以解决此问题的一种方法是将查询放在后台线程上,而不是等待ConfigureAwait(false)
为您执行此操作。
public async void RunQueryAsync(string Query)
{
Data = await Task.Run(() => OpenQueryAsync("<ConectionString>", (Query as string)));
}
我还删除了ConfigureAwait(false)
因为你希望你的INotifyPropertyChanged
在UI线程上发生。
你真的需要处理你的一次性物品。
protected async Task<DataTable> OpenQueryAsync(string ConnectionString, string Query)
{
using(OdbcConnection connection = new OdbcConnection(ConnectionString))
{
await connection.OpenAsync().ConfigureAwait(false);
using(OdbcCommand command = new OdbcCommand(Query, connection))
using(DbDataReader dataReader = await command.ExecuteReaderAsync().ConfigureAwait(false))
{
DataTable resultData = new DataTable();
resultData.Load(dataReader);
return resultData;
}
}
}
答案 1 :(得分:5)
您遇到的行为的原因是DbConnection
和DbCommand
类中的一个缺陷,所有ADO提供程序都将这些缺陷用作其特定类的基础。缺点是默认情况下所有Async
方法都是同步!它甚至记录在案!
例如,DbConnnection.OpenAsync的文档:
默认实现调用同步打开调用并返回已完成的任务。
和DbCommand.ExecuteDbDataReaderAsync:
默认实现调用同步 ExecuteReader方法并返回已完成的任务,阻止调用线程。
从我所看到的,只有SqlServer
提供程序使用真正的异步实现覆盖异步方法。但由于您使用的是OleDb
提供商,因此您运气不佳。