从 IActivatableViewModel 中的 WhenActivated 调用异步方法

Call async method from WhenActivated in IActivatableViewModel

我正在使用 AvaloniaUI 框架构建应用程序。
我有一个实现 IActivatableViewModel 的视图模型,我在视图模型构造函数中调用 WhenActivated 。我定义了一个方法 HandleActivation ,当视图模型被激活时调用,当视图模型被停用时调用 HandleDeactivation 。一切都被正确调用,那里没有问题。

看起来像这样:

public abstract class MyViewModel: ViewModelBase, IActivatableViewModel
    {
        public ViewModelActivator Activator { get; }

        protected MyViewModel() : base()
        {
            Activator = new ViewModelActivator();
            this.WhenActivated(disposables =>
            {
                this.HandleActivation();

                Disposable
                    .Create(this.HandleDeactivation)
                    .DisposeWith(disposables);
            });
        }

        private void HandleActivation()
        { }

        private void HandleDeactivation()
        { }
    }

我还有一个数据服务,可以从数据库中检索一些数据。我要调用的方法returns一个Task<T>。 它看起来像这样:

public interface IDataService 
{
     Task<IList<UserDto>> GetActiveUsers();
}

我想做的是从 HandleActivation 方法调用 GetActiveUsers。我需要获取用户列表,然后在检索到用户后对其进行一些 post 处理。
如果我在异步方法中这样做,我会做这样的事情

private async Task HandleActivation()
{
    var users = await _dataService.GetActiveUsers();
    foreach(var user in users)
    {
       //do stuff
    }
}

由于 HandleActivation 不是异步方法,我对如何去做这件事有点困惑。
有没有一种方法可以使 HandleActivation 异步,是否有一种我缺少的“反应式”方法。 我对 AvaloniaUI 和 ReactiveUI 以及反应式编程本身还很陌生,所以我确信有一种“正确”的方法可以做到这一点,但我很难弄明白。

第一个代码片段中关于处理激活和停用的模式与 ReactiveUI 文档中的示例相匹配,但是没有人阻止您将 HandleActivation 方法编写为异步的,因此它看起来像这样:

    protected MyViewModel() : base()
    {
        Activator = new ViewModelActivator();

        this.WhenActivated(disposables =>
        {
            this.HandleActivation().ConfigureAwait(false);

            Disposable
                .Create(this.HandleDeactivation)
                .DisposeWith(disposables);
        });
    }

    private async Task HandleActivation()
    {
        var users = await Task<IEnumerable<UserDto>>.Factory.StartNew(() => _dataService.GetActiveUsers().Result);

        foreach (var user in users)
        {
            // do stuff
        }
    }

使用可观察对象的更具反应性的方法可能如下所示:

    protected MyViewModel() : base()
    {
        Activator = new ViewModelActivator();

        this.WhenActivated(disposables =>
        {
            this.HandleActivation();

            Disposable
                .Create(this.HandleDeactivation)
                .DisposeWith(disposables);
        });
    }


    private void HandleActivation()
    {
        Observable.Start(() => _dataService.GetActiveUsers().Result)
            .ObserveOn(RxApp.MainThreadScheduler) // schedule back to main scheduler only if the 'stuff to do' is on ui thread
            .Subscribe(users => DoStuffWithTheUsers(users));
    }

    private void DoStuffWithTheUsers(IEnumerable<UserDto> users)
    {
        foreach (var user in users)
        {
            // do stuff
        }
    }

如果 GetActiveUsers 本身是返回 IList 的同步方法,这甚至可以异步工作,因为 Observable.Start 已经异步调用了该方法。代码示例中的唯一区别是必须删除“.Result”。

处理此类模式的另一种选择是使用 ReactiveCommand<Unit, Unit>。响应式命令很棒,它们让你在管理 long-running 异步操作方面拥有超强的能力。一个reactive command allows you to subscribe to the IsExecuting and ThrownExceptions observables and keep your users informed about what your app is doing. Also, reactive commands support cancelation。所以我可能建议你将激活逻辑移动到一个反应命令中,例如命名为Activate,将停用逻辑移动到名为 Deactivate 的响应式命令中,然后在 WhenActivated 块内,您可以这样做:

this.WhenActivated(disposable => {
    Activate.Execute().Subscribe(); // Handle async activation
    Disposable
        .Create(() => Deactivate.Execute().Subscribe()) // Handle async deactivation
        .DisposeWith(disposable);
});

另见