从 IActivatableViewModel 中的 WhenActivated 调用异步方法

问题描述 投票:0回答:2

我正在使用 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()
        { }
    }

我还有一个数据服务,可以从数据库中检索一些数据。我要调用的方法返回一个

Task<T>
。 看起来像这样:

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

我想做的是从 HandleActivation 方法调用

GetActiveUsers
。我需要获取用户列表,然后在检索后对其进行一些后处理。
如果我在异步方法中执行此操作,我会执行类似的操作

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

由于

HandleActivation
不是异步方法,我对如何执行此操作有点困惑。
有没有办法让
HandleActivation
异步,是否有我缺少的“反应式”方法。 我对 AvaloniaUI 和 ReactiveUI 以及反应式编程本身非常陌生,所以我确信有一个“正确”的方法来做到这一点,但我很难弄清楚。

c# reactiveui avaloniaui
2个回答
2
投票

第一个代码片段中处理激活和停用的模式与 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(disposables);

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


    private void HandleActivation(CompositeDisposable disposables)
    {
        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))
            .DisposeWith(disposables);
    }

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

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


2
投票

处理此类模式的另一种选择是使用

ReactiveCommand<Unit, Unit>
。反应式命令很棒,它们为您提供了管理长时间运行的异步操作的超能力。 反应式命令允许您订阅
IsExecuting
ThrownExceptions
可观察量,并让用户了解您的应用程序正在做什么。此外,反应式命令支持取消。因此,我可能建议您将激活逻辑移至名为例如的反应式命令中
Activate
,将停用逻辑移动到名为
Deactivate
的反应式命令中,然后在
WhenActivated
块内,您可以执行以下操作:

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

另请参阅 此 StackOverflow 问题

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