ReactiveUI Command with WCF resulting in Thread error on updating of Observable Property

StackOverflow https://stackoverflow.com/questions/21470901

  •  05-10-2022
  •  | 
  •  

Question

I am attempting to make a WCF call using ReactiveUI commands and capturing the resulting string with ObservableAsPropertyHelper. With the code below I am receiving the following error message -

"The calling thread cannot access this object because a different thread owns it"

The WCF call returns but errors on accessing the ObservableForProperty - Message and/or when raising its PropertyChanged

Let me know if anyone needs other details/code.

ViewModel: UserService.Authenticate is proxy call to a WCF endpoint

public class LoginViewModel : ReactiveObject, IRoutableViewModel
{
  public LoginViewModel(IScreen hostScreen , MainViewModel appRootViewModel, IUserService userService)
    {
        HostScreen = hostScreen;

        UserService = userService;
        Application = appRootViewModel;
        var canLogin = this.WhenAny(x => x.LoginName, x => x.Password, (l, p) =>
            !String.IsNullOrWhiteSpace(l.Value) && !String.IsNullOrWhiteSpace(p.Value));

        LoginCommand = new ReactiveCommand(canLogin);

        var loggedIn = LoginCommand.RegisterAsync(_ => Observable.Start(() =>
            {
                var request = new Request
                {
                    UserIdentity = new User.Identity
                    {
                        Login = LoginName,
                        Password = new User.Password { Old = Password }
                    }

                };
                var authenticationResult = UserService.Authenticate(request).Authenticated;

                return authenticationResult ? "Login Succeeded...Continuing"
                    : "Login Failed...Please try again";


            }));
        loggedIn.Subscribe(s =>
        {
            if (s == "Login Succeeded...Continuing to Analytics")
            {
                HostScreen.Router.Navigate.Execute(Application);
            }

        });

            message = new ObservableAsPropertyHelper<string>(loggedIn,
            s =>
            {

                raisePropertyChanged("Message");

            });

view code behind:

public partial class LoginView : IViewFor<LoginViewModel>
{
   public LoginView()
    {
        InitializeComponent();

        this.WhenAnyValue(x => x.ViewModel).BindTo(this, x => x.DataContext);
        this.Bind(ViewModel, model => model.Password, x => x.password.Text);
        this.Bind(ViewModel, model => model.LoginName, view => view.userName.Text);
        this.OneWayBind(ViewModel, model => model.Message, x => x.message.Content);
        this.OneWayBind(ViewModel, x => x.LoginCommand, x => x.login.Command);
    }

    public static readonly DependencyProperty ViewModelProperty =
DependencyProperty.Register("ViewModel", typeof(LoginViewModel), typeof(LoginView), new PropertyMetadata(null));


    object IViewFor.ViewModel
    {
        get { return ViewModel; }
        set { ViewModel = (LoginViewModel)value; }
    }

    public LoginViewModel ViewModel
    {
        get
        {
            return (LoginViewModel)GetValue(ViewModelProperty);
        }
        set
        {
            SetValue(ViewModelProperty,
                value);
        }
    }

}

}

Was it helpful?

Solution 2

Update: Fixed by telling the observer callbacks to run on the current synchronization context.

.ObserveOn(SynchronizationContext.Current)

So below is the LoginCommand Observable code to fix the above issues. Last line is the edit.

var loggedIn = LoginCommand.RegisterAsync(_ => Observable.Start(() =>
            {


                Session<NullT> init = new Session<NullT>
                {
                    SqlKey = System.Configuration.ConfigurationManager.AppSettings["sharedKey"].ToString()

                };

                var initResponse = UserService.Initialize(init);
                var authenticationResult = false;
                if (initResponse.SessionOk)
                {
                    initResponse.UserIdentity = new User.Identity
                    {
                        Login = LoginName,
                        Password = new User.Password { Old = Password }
                    };


                    authenticationResult = UserService.Authenticate(initResponse).Authenticated;
                    return authenticationResult ? "Login Succeeded"
                        : "Login Failed...Please try again";
                }
                else return "Failed to Initialize.";


            }).ObserveOn(SynchronizationContext.Current));

OTHER TIPS

Most of your code is correct (except for where you set message, just use loggedIn.ToProperty), but as I recall, WCF tries to "help you out" by fiddling with SynchronizationContexts, you need to disable this (I'm not sure how to do this though)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top