Wpf 实体框架异步调用阻止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 例如,我

我一直在慢慢地尝试将代码从使用动作委托转换为WPF应用程序中的新任务。我喜欢等待操作可以在同一个方法中运行,这大大减少了我需要的方法的数量,提高了可读性,减少了维护。尽管如此,我在调用EF6异步方法时遇到了代码问题。它们似乎都是同步运行的,并且阻塞了我的UI线程。我在代码中使用了以下技术/框架:

  • .NET Framework 4.5
  • WPF
  • MVVM Light 5.2
  • 通用工作单元/存储库框架v3.3.5
  • 实体框架6.1.1
  • SQL Server 2008 R2 Express
例如,我有一个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容器创建的通用存储库对象。登录方法如下所示:

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)和它是一样的。这只会生成另一个没有