0

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

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

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

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

4

2 回答 2

1

处理此类模式的另一种选择是使用ReactiveCommand<Unit, Unit>. 反应式命令很棒,它们为您提供了管理长时间运行的异步操作的超能力。响应式命令允许您订阅IsExecutingThrownExceptionsobservables,并让您的用户了解您的应用程序正在做什么。此外,反应式命令支持取消。所以我可能建议你将激活逻辑移动到一个名为 eg 的反应性命令Activate中,将停用逻辑移动到一个名为 的反应性命令Deactivate中,然后在WhenActivated块内你可以这样做:

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

另请参阅此 StackOverflow 问题

于 2020-09-29T21:08:26.630 回答
1

在您的第一个代码片段中处理激活和停用的模式与 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
        }
    }

使用 observables 的更具反应性的方法可能如下所示:

    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”。

于 2020-08-30T18:41:47.013 回答