MVVM Light Call异步方法的属性已更改?

时间:2019-03-27 03:42:48

标签: c# wpf mvvm async-await mvvm-light

因此,我一直在进行大量的挖掘工作,但在任何地方都找不到任何真正的确定答案。

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

这样,我设法将某些东西合在一起以使其正常工作,但是感觉有些乱七八糟。

在不使用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;
    }

如果我只是尝试在async方法上调用.Result,则会导致死锁,需要重新启动应用程序才能解决。尽管我的作品很有效,但我并不是真正的粉丝。我还有其他选择吗?

3 个答案:

答案 0 :(得分: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
   }  
});

注意 :从技术上讲,这是卸载的操作(它将失去上下文的注意),而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 :(得分:2)

事件处理程序允许使用异步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 :(得分:1)

看看Stephen Cleary编写的NotifyTask类。

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

您可以将代码重构为:

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,只有在{{1 }}已完成。