Wpf 实体框架异步调用阻止UI线程
我一直在慢慢地尝试将代码从使用动作委托转换为WPF应用程序中的新任务。我喜欢等待操作可以在同一个方法中运行,这大大减少了我需要的方法的数量,提高了可读性,减少了维护。尽管如此,我在调用EF6异步方法时遇到了代码问题。它们似乎都是同步运行的,并且阻塞了我的UI线程。我在代码中使用了以下技术/框架:Wpf 实体框架异步调用阻止UI线程,wpf,mvvm,async-await,entity-framework-6,mvvm-light,Wpf,Mvvm,Async Await,Entity Framework 6,Mvvm Light,我一直在慢慢地尝试将代码从使用动作委托转换为WPF应用程序中的新任务。我喜欢等待操作可以在同一个方法中运行,这大大减少了我需要的方法的数量,提高了可读性,减少了维护。尽管如此,我在调用EF6异步方法时遇到了代码问题。它们似乎都是同步运行的,并且阻塞了我的UI线程。我在代码中使用了以下技术/框架: .NET Framework 4.5 WPF MVVM Light 5.2 通用工作单元/存储库框架v3.3.5 实体框架6.1.1 SQL Server 2008 R2 Express 例如,我
- .NET Framework 4.5
- WPF
- MVVM Light 5.2
- 通用工作单元/存储库框架v3.3.5
- 实体框架6.1.1
- SQL Server 2008 R2 Express
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容器创建的通用存储库对象。登录方法如下所示:
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;
}
但我明白这不应该是必要的,因为我对数据库的调用是IO绑定的,而不是CPU绑定的。那么,我的应用程序有什么问题?为什么它会阻塞我的UI线程?您的
RelayCommand
不是异步的
LogInCommand = new RelayCommand(() => ExecuteLogInCommand());
因为没有async
/await
将同步调用ExecuteLogInCommand
你必须把它改成
LogInCommand = new RelayCommand(async () => await ExecuteLogInCommand());
因此,
RelayCommand
也被称为异步。您的RelayCommand
不是异步的
LogInCommand = new RelayCommand(() => ExecuteLogInCommand());
因为没有async
/await
将同步调用ExecuteLogInCommand
你必须把它改成
LogInCommand = new RelayCommand(async () => await ExecuteLogInCommand());
因此,
RelayCommand
也被称为异步。您的LogIn
和FindUser
(应该称为LogInAsync
和FindUserAsync
)不应该与UI一起工作。应该在所有等待中使用ConfigureAwait(false)
然而,在调用真正异步的东西之前,所有调用都是同步的。我想应该是SingleOrDefaultAsync
如果将其包装在
Task.Run
中会产生这样的差异,那么出于某种原因,SingleOrDefaultAsync
必须同步运行。您的登录名和FindUser
(应该称为LogInAsync
和FindUserAsync
)不应该与UI一起工作的应在所有等待中使用ConfigureAwait(false)
然而,在调用真正异步的东西之前,所有调用都是同步的。我想应该是SingleOrDefaultAsync
如果在Task.Run
中包装它会产生这样的差异,那么出于某种原因,SingleOrDefaultAsync
必须同步运行。存储库类上的.Queryable()
到底是什么?你管它叫什么?IRepositoryAsync
是否从其他接口继承,其具体功能是什么implementation@Tsengqueryable()只返回底层用户dbset,您可以对其应用任何实体框架方法调用。IRepositoryAsync是通用存储库/UnitOfWork框架提供的接口。它还提供了一个EF6实现。我没有在其中添加任何代码,因为它非常广泛。我提供了上面的链接,所以你可以看看它,如果你喜欢。谢谢您的模式在我看来很好,有一些建议:将生成时的语法缩短为新RelayCommand
;您不需要()=>
部件。此外,如果不修改返回的对象,则可以删除存储库代码中的async
和wait
关键字。通过这个小小的更改,您将保存生成一个在这种情况下不必要的任务对象。对于async
问题:您可以尝试使用IRepositoryAsync
上的Select
方法,而不是Queryable()
。存储库类上的.Queryable()
到底是什么?你管它叫什么?IRepositoryAsync
是否从其他接口继承,其具体功能是什么implementation@Tsengqueryable()只返回底层用户dbset,您可以对其应用任何实体框架方法调用。IRepositoryAsync是通用存储库/UnitOfWork框架提供的接口。它还提供了一个EF6实现。我没有在其中添加任何代码,因为它非常广泛。我提供了上面的链接,所以你可以看看它,如果你喜欢。谢谢您的模式在我看来很好,有一些建议:将生成时的语法缩短为新RelayCommand
;您不需要()=>
部件。此外,如果不修改返回的对象,则可以删除存储库代码中的async
和wait
关键字。通过这个小小的更改,您将保存生成一个在这种情况下不必要的任务对象。对于您的async
问题:您可以尝试使用IRepositoryAsync
上的Select
方法,而不是Queryable()
。这只会生成另一个没有实际效果的状态机。@PauloMorgado:但是如果调用ExecuteLogInCommand
时没有wait
或链接到。ContinueWith()
它将自动同步运行,无论下面的调用是否异步。当使用async/await时,您必须始终使用asyncdown@Tseng我尝试使用async/await关键字使命令调用异步。它仍然会阻塞。@Tseng,async
修饰符唯一做的事情就是生成一个状态机。代码可以是LogInCommand=newrelaycommand(ExecuteLogInCommand)代码>和它是一样的。这只会生成另一个没有