Easily decouple your MVVM ViewModel from your Model using RX Extensions
Apr
25
Written by:
4/25/2010 1:11 PM
With “Simplified MVVM” you can simply place your web service methods in your Model. The problem you run into, is how do you make an asynchronous web service call and fill a collection in your ViewModel? One method I have employed in the past is to pass an instance of the ViewModel to the Model, however, the problem this causes, is that you have now tightly coupled your ViewModel and your Model. It is also difficult to consume your Model from multiple ViewModels when you do it this way.
What you really want to do, is place your web service methods in your Model and simply call them from your ViewModel. RX Extensions allow you to do that.
First you want to download RX Extensions and install them from here: http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx

Next, add references in your Silverlight project to:
- System.CoreEx
- System.Observable
- System.Reactive
For the sample project, this is the web service method in the Model:
#region SearchWebsites
public static IObservable<IEvent<SearchWebsitesCompletedEventArgs>>
SearchWebsites(string SearchString)
{
// Set up web service call
WebsiteServiceClient objWebsiteServiceClient =
new WebsiteServiceClient();
// Get the base address of the website that launched the Silverlight Application
EndpointAddress MyEndpointAddress = new
EndpointAddress(GetBaseAddress());
// Set that address
objWebsiteServiceClient.Endpoint.Address = MyEndpointAddress;
// Set up a Rx Observable that can be consumed by the ViewModel
IObservable<IEvent<SearchWebsitesCompletedEventArgs>> observable =
Observable.FromEvent<SearchWebsitesCompletedEventArgs>(objWebsiteServiceClient,
"SearchWebsitesCompleted");
objWebsiteServiceClient.SearchWebsitesAsync(SearchString);
return observable;
}
#endregion
// Utility
#region GetBaseAddress
private static string GetBaseAddress()
{
string strXapFile = @"/ClientBin/SimpleRxExample.xap";
string strBaseWebAddress =
App.Current.Host.Source.AbsoluteUri.Replace(strXapFile, "");
return string.Format(@"{0}/{1}", strBaseWebAddress, @"WebsiteService.svc");
}
#endregion
Notice the web service method returns IObservable. This allows you to “subscribe” to the results from the ViewModel like this:
#region SearchWebsites
private void SearchWebsites(string SearchString)
{
// Clear the current Websites
colWebsites.Clear();
// Call the Model to get the collection of Websites
WebSites.SearchWebsites(SearchString).Subscribe(p =>
{
if (p.EventArgs.Error == null)
{
// loop thru each item
foreach (var Website in p.EventArgs.Result)
{
// Add to the colWebsites collection
colWebsites.Add(Website);
}
// If we have any Websites,
// set the selected item value to the first one
if (colWebsites.Count > 0)
{
SetWebSite(colWebsites[0]);
SelectedWebsiteInListProperty = 0;
}
else
{
// Set blank default values
SelectedWebsiteProperty = new Websites();
SelectedWebsiteInListProperty = -1;
}
}
});
}
#endregion
You are now able to easily call the web service in the Model from multiple ViewModels. You can see the live example here:
http://silverlight.adefwebserver.com/simplerxexample/
You can download the full source code here:
http://silverlight.adefwebserver.com/simplerxexample/SimpleRxExample.zip
9 comment(s) so far...
Re: Easily decouple your MVVM ViewModel from your Model using RX Extensions
I'm probably missing something, but can't the async call return before the subscription is made? In this case you're basically subscribing too late and won't be notified about the result. I know it's very unlikely that it WILL return before the subscription method is called, but it's not impossible.
By Adrian Hara on
6/22/2010 4:04 AM
|
Re: Easily decouple your MVVM ViewModel from your Model using RX Extensions
@Adrian Hara - The subscription will not fire until someone subscribes to it. You can download the code and set a break point. You will notice nothing happens until the Search button is clicked.
By Michael Washington on
6/22/2010 4:07 AM
|
Re: Easily decouple your MVVM ViewModel from your Model using RX Extensions
Hmm, I'm not sure I understand what you mean by "the subscription will not fire until someone subscribes to it". first impression was that in the time between creating an IObservable from an event and actually subscribing to it any events fired are "lots", i.e. the observable doesn't "store" the events so it can forward them to the observer "later". That's why I thought your example wouldn't work.
Now I took the code and it actually works, but I suspect this has something to do with the fact how Silverlight actually makes the webservice call, i.e. even though the name of the method is XXAsync, which might lead you to believe that it fires it on a thread right away, I think it's actually using a message loop to for the invoke, which means that it will only actually be invoked after the current stack frame is gone, which means the .Subscribe() call will already have been made, hence it will work.
Am I on to something here or talking complete rubbish? :)
By Adrian Hara on
6/23/2010 12:36 PM
|
Re: Easily decouple your MVVM ViewModel from your Model using RX Extensions
@Adrian Hara - If you put a breakpoint inside the method that "creates" the observable (not the one subscribing to it), you will see that it only gets fired when it is called by the code that subscribes to it. So it's like:
1) Hey I want to subscribe to this "Observable" 2) Oh hey someone is subscribed, I will let them know if something happens
(in this case the "Event" is when the web service method COMPLETES, We could have used another event such as when the web service starts or when it simply has a record to pass to us)
3) Then inside the method that returns the Observable there is code that says "SearchWebsitesAsync" and that eventually raises the Event
(this is the confusing part because this COULD have been raised outside the method and some would say that is the proper place for it so you would not have to re-subscibe each time. I argue that it is a layer of abstraction that is hard to follow and could be MORE code)
But, #2 and #3 did not happen until the code that subscribed to it was actually called. Until then, the Observable code was only code that could "potentially" run.
By Michael Washington on
6/23/2010 12:46 PM
|
Re: Easily decouple your MVVM ViewModel from your Model using RX Extensions
Again I'm not sure I follow, but take this example: if inside your SearchWebSites method, instead of creating an Observable from the webservice client you create it for a random class that has a DoXAsync() method which it runs on another thread and which raises a XAsyncCompleted event when it's done, then it's possible that the code in the Subscribe() lambda will never get called. Example below:
class Dummy { public event EventHandler SomeEvent;
public void DoAsync() { Task.Factory.StartNew(() => { if (SomeEvent != null) SomeEvent(this, EventArgs.Empty); }); } }
class Program { static void Main(string[] args) { var d = new Dummy();
var observable = Observable.FromEvent(d, "SomeEvent"); d.DoAsync(); Thread.Sleep(1000); observable.Subscribe(p => Console.WriteLine("event handler"));
Console.WriteLine("done"); Console.ReadLine(); } }
However, in the SL case, since the webservice call is actually done using the message pump, the call won't happen immediately, which gives your .Subscribe() call a chance to run.
At least that's what I think happens...
By Adrian Hara on
6/24/2010 4:18 AM
|
Re: Easily decouple your MVVM ViewModel from your Model using RX Extensions
Ah i forgot, if i change the original code to this:
var observable = WebSites.SearchWebsites(SearchString); observable.Subscribe(p =>...
...then the code which creates the observable runs BEFORE the call to Subscribe(). At least for me it does, maybe I don't have the correct version of the RX framework and this behavior changed in the meantime?
By Adrian Hara on
6/24/2010 4:18 AM
|
Re: Easily decouple your MVVM ViewModel from your Model using RX Extensions
@Adrian Hara - When I debug, the web service is called only after the subscription, and since I am using an "Anonymous Method" to handle the return, I don't see how there could ever be a problem.
However, I did create this thread: social.msdn.microsoft.com/Forums/en-US/rx/thread/6c1de56f-89b5-4f1c-889b-daeaf25df1d3
By Michael Washington on
6/24/2010 4:58 AM
|
Re: Easily decouple your MVVM ViewModel from your Model using RX Extensions
Right, the webservice is called only after because of how the queuing mechanism using the Dispatcher works in Silverlight. However, should this mechanism change in the future (which is unlikely :p), I guess subtle bugs could appear...
By Adrian Hara on
6/25/2010 4:03 AM
|
Re: Easily decouple your MVVM ViewModel from your Model using RX Extensions
wow thank you ^^
By guest on
8/4/2010 4:09 AM
|