MvvmCross - Calling Web Service from View Model

2019-02-07 09:57发布

问题:

I'm new to MvvmCross and Android development. I have a need to call to POST data to a JSON web service in my view model. I then need to display the result of the web service back in my UI. The gist of my view model looks like the following:

public class MyViewModel : MvxViewModel
{
  public override void Start()
  {
    base.Start();
  }

  public event EventHandler<EventArgs> Service_Finished;
  public void CallService()
  {
    string url = GetServiceUrl();

    WebRequest serviceRequest = HttpWebRequest.Create(url);
    serviceRequest.Method = "POST";
    serviceRequest.ContentType = "application/json";
    serviceRequest.BeginGetRequestStream(new AsyncCallback(ServiceBeginGetRequestStreamCallback), serviceRequest);
  }

  private void ServiceBeginGetRequestStreamCallback(IAsyncResult ar)
  {
    string json = GetJson();

    HttpWebRequest myWebRequest = (HttpWebRequest)(ar.AsyncState);
    using (Stream postStream = myWebRequest.EndGetRequestStream(ar))
    {
      byte[] byteArray = Encoding.UTF8.GetBytes(json);
      postStream.Write(byteArray, 0, byteArray.Length);
    }
    myWebRequest.BeginGetResponse(new AsyncCallback(Service_Completed), myWebRequest);
  }

  private void Service_Completed(IAsyncResult result)
  {
    if (Service_Finished != null)
      Service_Finished(this, new EventArgs());
  }
}

In my View (UI) code, I've added an event-handler for the Service_Finished event. I've noticed that I can successfully throw the event from the "CallService" method in my view model. However, if I try to fire Service_Finished from either the ServiceBeginGetRequestStreamCallback or the Service_Completed portions, the event is never fired in the UI.

Due to the fact that the view model is in a portable class library, I can't figure out how to debug this. At this point I know that CallService is getting successfully called. However, I can't tell where I'm getting within ServiceBeginGetRequestStreamCallback and if I'm even getting to Service_Completed.

I know from my Windows Phone dev experience, I would need to check to see if I'm on the UI thread, if not, I'd have to do some Deployment.stuff. But, with the MvvmCross approach, I'm not sure a) if I have to do that and b) if that is even an option since the view model should work with both Android and iOS. Regardless, there has to be a way to a) call a web service from a view model and b) send a message back to the view so that the UI can be updated. Unfortunately, I can't seem to figure it out. Can someone (slodge :)) tell me what I'm doing wrong?

Thank you

回答1:

In general, I put this kind of WebService call in the Model rather than in the ViewModel - it makes both the ViewModel and the WebService client code much more reusable.

Some simple examples of this are in:

  • the twittersearch sample - https://github.com/slodge/MvvmCross/tree/v3/Sample%20-%20TwitterSearch
  • the Dilbert sample - https://github.com/slodge/MvvmCross-Tutorials/tree/master/DailyDilbert

I know from my Windows Phone dev experience, I would need to check to see if I'm on the UI thread, if not, I'd have to do some Deployment.stuff. But, with the MvvmCross approach, I'm not sure a) if I have to do that and

Yes, all communication from ViewModel->View should be on the UI thread.

b) if that is even an option since the view model should work with both Android and iOS.

MvvmCross provides an interface to allow you to marshal execution onto the UI thread. In a ViewModel, this is easily done by calling InvokeOnMainThread(() => { /* your code */ })

Behind the scenes, MvvmCross will also marshall all RaisePropertyChanged executions to the UI thread too. Note - that ObservableCollection updates are not automatically marshalled though - this is because ObservableCollection is a class that exists outside of MvvmCross.

Regardless, there has to be a way to a) call a web service from a view model and

See the samples (above)

b) send a message back to the view so that the UI can be updated.

In general you should not send these type of messages via events.

Instead, you should:

  • update ViewModel properties
  • (occasionally) send Messages via a Messenger