【问题标题】:Async method does not return control flow to form异步方法不会将控制流返回到表单
【发布时间】:2013-11-14 10:36:33
【问题描述】:

我在 WPF 应用程序中的异步方法有问题。我想在我的 WPF 应用程序中实现异步等待模式,以在使用 EntityFramework 查询数据库时保持表单响应能力。我按照微软示例中的描述做了所有事情,但它没有像示例应用程序那样将控制流返回给应用程序。

这是我的按钮点击代码:

private async void LoginButton_Click(object sender, RoutedEventArgs e)
{
    var loggedIn = await _gluUserRepository.LoginAsync(LoginTextBox.Text, PasswordTextBox.Text);

    Switcher.Switch(new Loader());
    if (loggedIn)
    {
        UserName = LoginTextBox.Text;
        Switcher.Switch(new Blank());
    }
    else
    {
        UserName = String.Empty;
        MessageBox.Show("Błędny login lub hasło. Spróbuj ponownie.");
        Switcher.Switch(new Login());
    }
}

这是我的 LoginAsync 方法:

public async Task<bool> LoginAsync(string login, string password)
{
    string hashedPassword = PasswordHasher.Hash(password);
    var user = await _dbContext.Users.FirstOrDefaultAsync(x => x.UserName == login);
    if (user != null && user.Password == hashedPassword)
        return true;
    return false;
}

我在 Microsoft 示例应用程序中看到了 async/await 的相同用法,但是,他们的 WPF 应用程序将控制权返回给 Window 句柄,即我可以在我的应用程序中移动窗口,这是不可能的。

我想要实现的是在 WPF 应用程序中使用 async/await 模式来保持应用程序的响应能力。我想在查询数据库时显示加载圆,查询完成后返回方法。

有人知道我做错了什么吗?

【问题讨论】:

标签: c# wpf entity-framework asynchronous async-await


【解决方案1】:

如果我理解正确

 Switcher.Switch(new Loader());

是显示“加载圈”的内容。

您需要在await 登录之前调用它。就像现在一样,您的处理程序会在 LoginAsync 返回后执行其他所有操作。

您可能还想查看ReactiveUI。它提供了一个有用的框架来解决您的问题。

【讨论】:

  • 是的,你理解正确,但是,我尝试将它放在 await 之前,它仍然阻塞主线程,我无法将主窗口移动到任何地方。
  • 我怀疑有其他东西阻塞了,或者更有可能阻塞了 UI 线程,使其无响应。当你摆脱除了等待调用之外的所有东西时会发生什么?
  • 它仍然阻塞,所以我猜它在下面的某个地方。
【解决方案2】:

很难猜测,但请尝试更改 LoginButton_Click 代码的开头以启动任务,并更改 await 以获取稍后的结果,如下所示:

private async void LoginButton_Click(object sender, RoutedEventArgs e)
{
    var loggedInTask = _gluUserRepository.LoginAsync(LoginTextBox.Text, PasswordTextBox.Text);

    MessageBox.Show("before await");
    var loggedIn = await loggedInTask;
    MessageBox.Show("after await");

    // ...
}

您应该看到两个消息框,一个接一个,它们都应该是响应式的,即可移动和可点击。如果您确实看到了这种行为,那么问题很可能出在您的 Switcher.Switch 上,与 EntityFramework 的异步方法无关。

【讨论】:

    【解决方案3】:

    我找到了解决方案。虽然我不知道为什么它实际上有效。但它不会阻塞 UI 线程。我不认为它也是线程安全的,我们应该厌倦了。

    private async void LoginButton_Click(object sender, RoutedEventArgs e)
            {
                var loginTask = userRepository.LoginAsync(LoginTextBox.Text, PasswordTextBox.Password);
                controller.DisplayPageLoader();
                DbUser loginResult = await loginTask;
                if (loginResult != null)
                {
                    controller.DisplayPageNewMeal();
                    controller.SetLoggedUser(loginResult);
                }
                else
                {
                    MessageBox.Show("Błędny login lub hasło. Spróbuj ponownie.");
                    controller.DisplayPageLogin();
                }
            }
    

    然后在存储库中

    public Task<DbUser> LoginAsync(string login, string password)
            {
                return Task.Run<DbUser>( () => Login(login, password));
            }
    
    private DbUser Login(string login, string password)
            {
                try
                {
                    string hashedPassword = PasswordHasher.Hash(password);
                    var user = _dbContext.Users.FirstOrDefaultAsync(x => x.UserName == login);
                    if (user.Result != null && user.Result.Password == hashedPassword)
                        return user.Result;
                    return null;
                }
                catch(Exception ex)
                {
                    _logger.Error("Blad logowania uzytkownika", ex);
                    return null;
                }
            }
    

    【讨论】:

      【解决方案4】:

      我有异步加载方法的主表单:

      private async void MainMenu_Load(object sender, EventArgs e) { 等待_connection.Start(); 等待 _myHub.Invoke("加入"); _myHub.On("收到", 数据 => { var obj = JsonConvert.DeserializeObject(data);

                  messagewindows = new SendMessage(User, obj.Sender);
      
      
                       if ((obj.Reciever == User ) )
                       {
                          messagewindows. txtHistory.Text += obj.Sender + " :" + obj.text + Environment.NewLine;
                          messagewindows.Show();
                       }
      
      
      
              });
      
          }
      

      我为显示消息显示第二种形式,但形式挂起崩溃应用程序:

      private async void SendMessage_Load(object sender, EventArgs e) { 文本 = 接收者;

              await _connection.Start();
              await _myHub.Invoke("Join");
      
      
      
              _myHub.On("recieved", data =>
              {
                  var obj = JsonConvert.DeserializeObject<MessageSent>(data);
                  if ((obj.Reciever == Sender || obj.Sender == Sender) && (obj.Sender == Reciever || obj.Reciever == Reciever))
                  {
      
      
                          txtHistory.Text += obj.Sender + " :" + obj.text + Environment.NewLine;
      
                          txtHistory.SelectionStart = txtHistory.Text.Length;
                          txtHistory.ScrollToCaret();
      
                  }
              });
      
          }
      

      【讨论】:

        猜你喜欢
        • 2014-09-22
        • 1970-01-01
        • 1970-01-01
        • 2014-10-22
        • 1970-01-01
        • 2012-08-08
        • 1970-01-01
        • 2019-10-25
        • 1970-01-01
        相关资源
        最近更新 更多