ReactiveUI синхронный вызов ReactiveCommand вызывает System.InvalidOperationException

154
13 декабря 2018, 17:00

Предположим у меня есть Button с привязкой к команде ViewModel. Во ViewModel я пишу:

public ICommand ClickCommand { get; set; }
public bool IsClicked;
public MainViewModel()
{
    ClickCommand = ReactiveCommand.Create(OnClick);
}
private void OnClick()
{
    //символизирует некую быструю операцию
    IsClicked = true;
}

Я ожидаю, что код будет выполнен синхронно, более того я вообще ничего не меняю в интерфейсе, просто хочу выполнить быструю операцию. Но такой код падает с ошибкой:

System.InvalidOperationException: 'Вызывающий поток не может получить доступ к данному объекту, так как владельцем этого объекта является другой поток.'

потому что ReactiveCommand.Execute там внутри у себя лезет в свойство команды CanExecute, что приводит к вызову метода кнопки System.Windows.Controls.Primitives.ButtonBase.UpdateCanExecute() (все это видно по стеку вызова в ошибке). Это все очень хорошо, не могу никак понять, почему это делается не в UI Thread и как пропихнуть этот вызов все таки в UI Thread.

В конструкторе ReactiveCommand по умолчанию, если не передавать свой scheduler будет выбран RxApp.MainThreadScheduler.

Вопрос, кто-нибудь сталкивался с таким поведением и как добиться правильной работы?

UPD: полный stacktrace ошибки

System.InvalidOperationException
  HResult=0x80131509
  Message=Вызывающий поток не может получить доступ к данному объекту, так как владельцем этого объекта является другой поток.
  Source=WindowsBase
  StackTrace:
   at System.Windows.Threading.Dispatcher.VerifyAccess()
   at System.Windows.DependencyObject.GetValue(DependencyProperty dp)
   at System.Windows.Controls.Primitives.ButtonBase.get_Command()
   at System.Windows.Controls.Primitives.ButtonBase.UpdateCanExecute()
   at ReactiveUI.ReactiveCommand.OnCanExecuteChanged()
   at System.Reactive.AnonymousSafeObserver`1.OnNext(T value) in D:\a\1\s\Rx.NET\Source\src\System.Reactive\AnonymousSafeObserver.cs:line 46
   at System.Reactive.Subjects.FastImmediateObserver`1.EnsureActive(Int32 count) in D:\a\1\s\Rx.NET\Source\src\System.Reactive\Subjects\ReplaySubject.cs:line 889
   at System.Reactive.Subjects.ReplaySubject`1.ReplayBase.OnNext(T value) in D:\a\1\s\Rx.NET\Source\src\System.Reactive\Subjects\ReplaySubject.cs:line 273
   at System.Reactive.Linq.ObservableImpl.DistinctUntilChanged`2._.OnNext(TSource value) in D:\a\1\s\Rx.NET\Source\src\System.Reactive\Linq\Observable\DistinctUntilChanged.cs:line 79
   at System.Reactive.Linq.ObservableImpl.CombineLatest`3._.SecondObserver.OnNext(TSecond value) in D:\a\1\s\Rx.NET\Source\src\System.Reactive\Linq\Observable\CombineLatest.cs:line 180
   at System.Reactive.Subjects.FastImmediateObserver`1.EnsureActive(Int32 count) in D:\a\1\s\Rx.NET\Source\src\System.Reactive\Subjects\ReplaySubject.cs:line 889
   at System.Reactive.Subjects.ReplaySubject`1.ReplayBase.OnNext(T value) in D:\a\1\s\Rx.NET\Source\src\System.Reactive\Subjects\ReplaySubject.cs:line 273
   at System.Reactive.Linq.ObservableImpl.DistinctUntilChanged`2._.OnNext(TSource value) in D:\a\1\s\Rx.NET\Source\src\System.Reactive\Linq\Observable\DistinctUntilChanged.cs:line 79
   at System.Reactive.Linq.ObservableImpl.Select`2.Selector._.OnNext(TSource value) in D:\a\1\s\Rx.NET\Source\src\System.Reactive\Linq\Observable\Select.cs:line 49
   at System.Reactive.Linq.ObservableImpl.Scan`2._.OnNext(TSource value) in D:\a\1\s\Rx.NET\Source\src\System.Reactive\Linq\Observable\Scan.cs:line 50
   at System.Reactive.SafeObserver`1.OnNext(TSource value) in D:\a\1\s\Rx.NET\Source\src\System.Reactive\Internal\SafeObserver.cs:line 41
   at System.Reactive.ScheduledObserver`1.Dispatch(ICancelable cancel) in D:\a\1\s\Rx.NET\Source\src\System.Reactive\Internal\ScheduledObserver.cs:line 93
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()
Answer 1

На забугорном SO мне ответили - установи пакет ReactiveUI.Wpf. Установил - и все полетело.

Answer 2

Попробуйте так

private SynchronizationContext _context = SynchronizationContext.Current;
//…
private void OnClick()
{
    _context.Send(YouSpeedyDoingsMethod, speedyParams);
}
READ ALSO
Из Visual Studio не создается бд FireBird

Из Visual Studio не создается бд FireBird

Не получается из VS подключиться к бд

142
Подсказка внутри DataGrid wpf

Подсказка внутри DataGrid wpf

при создании новой строки я установил в ячейку подсказку с текстом с помощью XAML стиля FallbackValue=Name, цвет подсказки стандартный черный, возможно...

126
C# DataGridView изменяет обьект в другой форме

C# DataGridView изменяет обьект в другой форме

Почему всегда изменяется (если изменять)

164
ошибка при удалении VkDebugReportCallbackEXT

ошибка при удалении VkDebugReportCallbackEXT

при вызове метода vkDestroyDebugReportCallbackEXT выдаёт ошибку

132