我一直在慢慢尝试将代码从使用操作委托转换为WPF应用程序中的新任务。我喜欢await操作可以在同一方法中运行的事实,大大减少了我需要的方法数量,增强了可读性并减少了维护。话虽这么说,我在调用EF6异步方法时遇到了代码问题。他们似乎都同步运行并阻止我的UI线程。我在代码中使用了以下技术/框架:
作为示例,我有一个LogInViewModel,其命令在我的WPF应用程序上单击一个按钮后执行。这是在构造函数中初始化的命令:
LogInCommand = new RelayCommand(() => ExecuteLogInCommand());
这是命令体:
private async void ExecuteLogInCommand()
{
// Some code to validate user input
var user = await _userService.LogIn(username, password);
// Some code to confirm log in
}
用户服务使用使用MVVM Light的SimpleIoC容器创建的通用存储库对象。 LogIn方法如下所示:
public async Task<User> LogIn(string username, string password)
{
User user = await _repository.FindUser(username);
if (user != null && user.IsActive)
{
// Some code to verify passwords
return user;
}
return null;
}
我的存储库代码登录:
public static async Task<User> FindUser(this IRepositoryAsync<User> repository, string username)
{
return await repository.Queryable().Where(u => u.Username == username).SingleOrDefaultAsync();
}
SingleOrDefaultAsync()调用是Entity Framework的异步调用。此代码阻止了我的UI线程。我已阅读Stephen Cleary和其他人关于异步等待和正确使用的多篇文章。我一直尝试使用ConfigureAwait(false),没有运气。我让我的RelayCommand调用使用async await关键字没有运气。我已经分析了代码,返回时间最长的行是SingleOrDefaultAsync()行。其他一切几乎都是瞬间发生的。在我的代码中对DB进行其他异步调用时遇到同样的问题。唯一可以立即修复它的是:
User user = await Task.Run(() =>
{
return _userService.LogIn(Username, p.Password);
});
但是我知道这不应该是必要的,因为我对数据库的调用是IO绑定而不是CPU绑定。那么,我的应用程序有什么问题,为什么它会阻止我的UI线程?
答案 0 :(得分:0)
您的RelayCommand
不是异步的。
LogInCommand = new RelayCommand(() => ExecuteLogInCommand());
由于没有async
/ await
您的ExecuteLogInCommand
将被同步调用。
你必须把它改成
LogInCommand = new RelayCommand(async () => await ExecuteLogInCommand());
这样RelayCommand
也称为异步。
答案 1 :(得分:0)
您的dir1
和LogIn
(according to the guidelines应该被称为FindUser
和LogInAsync
)不应该使用UI应该使用所有等待FindUserAsync
。
但是,在调用真正异步的内容之前,所有调用都是同步的。我想那将是ConfigureAwait(false)
。
如果将其包装在SingleOrDefaultAsync
中会有所不同,那么,出于某种原因,Task.Run
必须同步运行。