属性上的MVVM Light Call异步方法改变了吗?

问题描述 投票:1回答:3

所以我一直在做很多挖掘,而且我无法在任何地方找到任何真正明确的答案。

我正在使用MVVM Light和WPF编写应用程序。我有一个注入我的ViewModel的服务,它检查设置的某个属性的有效性。该服务进行Web调用,它是异步的。该调用不需要停止执行该应用程序,而只是为了向用户提供关于输入值的有效性的视觉反馈。

因此,我设法将一些东西放在一起以使其工作,但感觉有点hackey。

在不诉诸async void之类的情况下,对属性更改执行异步方法的正确方法是什么?

这就是我现在拥有的。

    public string OmmaLicenseNumber
    {
        get => _ommaLicenseNumber;
        set
        {
            Set(() => OmmaLicenseNumber, ref _ommaLicenseNumber, value);
            Patient.OmmaLicenseNumber = value;

            var _ = CheckLicenseValid();
        }
    }

    private async Task CheckLicenseValid()
    {
        var valid = await _licenseValidationService.IsValidAsync(OmmaLicenseNumber);

        // We don't want the UI piece showing up prematurely. Need 2 properties for this;
        LicenseValid = valid;
        LicenseInvalid = !valid;
    }

如果我只是尝试在异步方法上调用.Result,则会导致死锁,需要重新启动应用程序才能修复。虽然我的工作,我不是一个粉丝。我还有什么其他选择?

c# wpf mvvm async-await mvvm-light
3个回答
2
投票

事件处理程序允许使用async void

参考Async/Await - Best Practices in Asynchronous Programming

public string OmmaLicenseNumber {
    get => _ommaLicenseNumber;
    set {
        Set(() => OmmaLicenseNumber, ref _ommaLicenseNumber, value);
        Patient.OmmaLicenseNumber = value;
        //Assuming event already subscribed 
        //i.e. OmmaLicenseNumberChanged += OmmaLicenseNumberChanged;
        OmmaLicenseNumberChanged(this, 
            new LicenseNumberEventArgs { LicenseNumber = value }); //Raise event
    }
}

private event EventHandler<LicenseNumberEventArgs> OmmaLicenseNumberChanged = delegate { };
private async void OnOmmaLicenseNumberChanged(object sender, LicenseNumberEventArgs args) {
    await CheckLicenseValid(args.LicenseNumber); //<-- await async method call
}

private async Task CheckLicenseValid(string licenseNumber) {
    var valid = await _licenseValidationService.IsValidAsync(licenseNumber);

    // We don't want the UI piece showing up prematurely. Need 2 properties for this;
    LicenseValid = valid;
    LicenseInvalid = !valid;
}

//...

public class LicenseNumberEventArgs : EventArgs {
    public string LicenseNumber { get; set; }
}

我认为这有点重手吗?

是。这仅仅是为了表明它是可行的。

这可以重构为一些更简单的帮助/实用程序方法调用吗?

是。可能看起来很像一个等待回调模式使用表达式获取值来验证


2
投票

这里的问题不是如何运行未观察到的async任务,它是如何处理异常的。我这样说是因为当任务被清理时它们可能会出现。

理想情况下,只需向下一位读者展示他们所获得的内容。既然你反对使用async void

选项1

// running an async method unobserved 
Task.Run(async () =>
{
   try
   {
      await DoSomethingAsync();
   }
   catch (Exception e)
   {
       // Deal with unobserved exceptions 
       // or there will be dragons
   }  
});

注意:这在技术上是卸载的(它将失去上下文BEWARE)并且async lamda无论如何都会使它成为异步空白,但是在任何情况下你都必须处理异常

选项2和更具争议性

public async void DoSomethingFireAndForget()
{
   try
   {
      await DoSomethingAsync();
   }
   catch (Exception e)
   {
      // Deal with unobserved exceptions 
      // or the will be dragons
   }  
}

选项3最好的两个世界。

注意:使用您需要的管道来观察异常,即Action

public static class TaskUtils
{

#pragma warning disable RECS0165 // Asynchronous methods should return a Task instead of void
   public static async void FireAndForgetSafeAsync(this Task task,  Action<Exception> onErrors = null)
#pragma warning restore RECS0165 // Asynchronous methods should return a Task instead of void
   {
      try
      {
         await task;
      }
      catch (Exception ex)
      {
         onErrors?.Invoke(ex);
      }
   }
}

也许Stephen Cleary将会在此发生,并给你一个更有说服力的解释


1
投票

看看Stephen Cleary写的NotifyTask-Class。

这是在构造函数和属性中处理异步操作的好方法。

你可以重构你的代码:

private NotifyTask _OmmaLicenseNumberChangedNotifyTask
    = NotifyTask.Create(Task.CompletedTask);
public NotifyTask OmmaLicenseNumberChangedNotifyTask
{
    get => this._OmmaLicenseNumberChangedNotifyTask;
    set
    {
        if (value != null)
        {
            this._OmmaLicenseNumberChangedNotifyTask = value;
            OnPropertyChanged("OmmaLicenseNumberChangedNotifyTask");
        }
    }
}

public string OmmaLicenseNumber
{
    get => _ommaLicenseNumber;
    set
    {
        Set(() => OmmaLicenseNumber, ref _ommaLicenseNumber, value);
        Patient.OmmaLicenseNumber = value;

        OmmaLicenseNumberChangedNotifyTask = NotifyTask.Create(CheckLicenseValid());
    }
}

然后你可以忘记Task或者你可以将你的UI的元素绑定到NotifyTask的属性,就像IsCompleted一样,只有当Task完成时才能看到一些东西。

© www.soinside.com 2019 - 2024. All rights reserved.