如何使用 async ReactiveCommand 解决可能的调度错误?

How to resolve possible scheduling error with async ReactiveCommand?

我对 ReactiveUI 比较陌生,我正在尝试从 ReactiveCommand 异步执行数据库查询。据我所知,问题不在于执行异步查询,而是当我尝试将结果加载到视图模型中的 ReactiveList 时。我认为这是一个调度问题,但我对 RxUI 不够熟悉,无法提出正确的方法。

我已经尝试使用 ObserveOn 与 RxApp.TaskPoolScheduler 和 RxApp.MainThreadScheduler 一起订阅视图模型中的命令,但似乎都没有帮助。

我的视图模型:

   public class UsersViewModel : ReactiveObject, IRoutableViewModel
   {
      ReactiveList<LisUser> _users;
      IUserManagementService UsersService { get; }

      public IScreen HostScreen { get; }

      public ReactiveCommand<Unit, IEnumerable<LisUser>> LoadUsers { get; }

      public String UrlPathSegment => "users";

      public ReactiveList<LisUser> Users
      {
         get => _users;
         set => this.RaiseAndSetIfChanged(ref _users, value);
      } 

      public UsersSubPageViewModel(
         IScreen screen,
         IUserManagementService usersService)
      {
         HostScreen = screen ?? throw new ArgumentNullException(nameof(screen));
         UsersService = 
            usersService ?? throw new ArgumentNullException(nameof(usersService));

         Users = new ReactiveList<LisUser>();
         LoadUsers = ReactiveCommand.CreateFromTask(async () =>
         {
            return await UsersService.GetUsersAsync().ConfigureAwait(false);
         });
         LoadUsers
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(list =>
            {
               Users.Clear();
               foreach (var u in list)
               {
                  Users.Add(u);
               }
            });
      }
   }

我的看法:

   public partial class UsersView : ReactiveUserControl<UsersViewModel>
   {
      public UsersPageView()
      {
         InitializeComponent();

         this.WhenActivated(disposables =>
         {
            this.WhenAnyValue(x => x.ViewModel.LoadUsers)
               .SelectMany(x => x.Execute())
               .Subscribe()
               .DisposeWith(disposables);
         });
      }
   }

我想要发生的是,当 UsersView 被激活时,UsersService 的 GetUsers 方法异步执行并将返回的用户列表加载到 Users ReactiveList 中。相反,我在 VS 中看到一个标题为 "Source Not Found" 的新选项卡和一条消息说 "RxApp.cs not found"。实际的例外是 System.Exception 和一条消息说明 "Cannot obtain value of the local variable or argument because it is not available at this instruction pointer, possibly because it has been optimized away."

所以,我的第一个问题是 "is this actually a scheduling issue?" 第二个问题是 "how do I resolve it?"

。首先,如我的评论中所述,反应列表已弃用。

调用 LoadUsers 命令的方式有问题。

一个是您可以在视图模型中执行 WhenActivated。您从 ISupportsActivation 派生视图模型,您的视图将在调用 WhenActivated 时调用视图内的 WhenActivated 块。有关想法,请参阅 https://reactiveui.net/docs/handbook/when-activated/#viewmodels

第二种方法是使用

this.WhenAnyValue(x => x.ViewModel.LoadUsers)
               .Select(x => x.Execute())
               .Switch()
               .Subscribe()
               .DisposeWith(disposables);

所以上面说获取 LoadUsers 命令,获取可观察的 Execute(),Switch() 意味着只订阅最新的,当有新值出现时处理旧的执行,订阅那个我们将 运行 命令,并设置为可能停止。

问题似乎与调度无关,而是与异步相关。

IUserManagementService 有一个 IUserRepository 的引用,它本身有一个 GetUsersAsync 方法来实际查询数据库。 IUserManagementService 的实际实现在调用 IUserRepository.GetUsersAsync 时缺少 ConfigureAwait。这在单元测试中不是问题,但一旦涉及 UI 就绝对是个问题。