Как устранить возможную ошибку планирования с помощью асинхронного ReactiveCommand? - PullRequest
0 голосов
/ 27 мая 2019

Я относительно новичок в 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 метод GetUsers службы UsersService выполнялся асинхронно и загружал возвращенный список пользователей в список пользователей ReactiveList. Вместо этого я вижу новую вкладку в VS с названием «Source Not Found» и сообщением, что «RxApp.cs not found». Фактическим исключением является System.Exception с сообщением о том, что «Невозможно получить значение локальной переменной или аргумента, потому что оно недоступно по указателю этой инструкции, возможно, потому что оно было оптимизировано».

Итак, мой первый вопрос: "это на самом деле проблема планирования?" И второй вопрос: «Как мне это решить?»

Ответы [ 2 ]

0 голосов
/ 29 мая 2019

Похоже, что проблема была не в планировании, а в асинхронности.

IUserManagementService имеет ссылку на IUserRepository, который сам имеет метод GetUsersAsync для фактического запроса к базе данных.Фактическая реализация IUserManagementService отсутствовала в ConfigureAwait при вызове IUserRepository.GetUsersAsync.Это не было проблемой в модульных тестах, но определенно проблема, когда был задействован пользовательский интерфейс.

0 голосов
/ 27 мая 2019

. Сначала, как указано в моем комментарии, реактивный список устарел.

Проблема в способе вызова команды LoadUsers.

Одним из них является то, что вы можете делать при активации вашей модели представления. Вы извлекаете модель представления из ISupportsActivation, ваше представление будет вызывать блок WhenActivation внутри вашего представления, когда вызывается ее WhenActivation. См. 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 () означает подписаться только на самое новое сначала и избавиться от старого выполнения, когда придет новое значение, подписаться на него мы будем запустите команду и избавьтесь, чтобы потенциально остановиться.

...