Asycnhronous Service Calls with Coroutines
Pete Brown just published a great blog post about calling WCF or Web services asynchronously, so I had to pull down his sample code and make it work with MVVM Light and the coroutine support from MadProps.MvvmLight.
The first thing I did was remove the "Click" event handler from his CallService
button, and instead define a command for it in the code-behind. Note that ordinarily this code would be in a ViewModel and you'd bind the command to the button, but I was just hacking away at sample code so this was nice and quick:
public MainWindow()
{
InitializeComponent();
var processor = new ActivityProcessor();
CallService.Command = processor.CreateCommand(CallServiceExecuted);
}
Then I defined the CallServiceExecuted method:
IEnumerable<IActivity> CallServiceExecuted()
{
var callService = new CallServiceActivity();
yield return callService;
CustomerList.ItemsSource = callService.Customers;
}
And lastly an activity to do the actual call, using Pete's callback code:
public class CallServiceActivity : ActivityBase
{
void ResponseCallback(IAsyncResult asyncResult)
{
CustomerRequestState asyncState = asyncResult.AsyncState as CustomerRequestState;
Customers = asyncState.Client.EndGetCustomers(asyncResult);
RaiseCompleted();
}
public Customer[] Customers { get; private set; }
public override void Execute(ActivityContext context)
{
var client = new Services.CustomerServiceClient();
CustomerRequestState requestState = new CustomerRequestState();
requestState.Client = client;
client.BeginGetCustomers(new AsyncCallback(ResponseCallback), requestState);
}
}
The activity itself could've been defined in any number of ways, but it was simple enough to steal Pete's code and write it in a similar fashion.
Note that there's no need to synchronize the call back to the UI thread, because the ActivitiyProcessor
class handles that for you via the default dispatcher. If you hide your activity away in a separate file somewhere, the actual logic looks very sequential and easy to read. Imagine how much less complex this would be if you had a few asynchronous calls to make, one after the other!
Update
For completeness, here's another, simpler way to define the CallServiceActivity
class:
public class CallServiceActivity : ActivityBase
{
public Customer[] Customers { get; private set; }
public override void Execute(ActivityContext context)
{
var client = new Services.CustomerServiceClient();
client.GetCustomersCompleted += (s, ev) =>
{
Customers = ev.Result;
RaiseCompleted();
};
client.GetCustomersAsync();
}
}
Trackbacks
- Asycnhronous Service Calls with Coroutines « Mas-Tool's Favorites | http://mas-tool.com/?p=2766
- Linksammlung 24/03/2011 | Silverlight, WPF & .NET | http://www.ebnerj.at/blog/?p=1143
- rimonabantexcellence site title | http://www.rimonabantexcellence.com/t.php?ahr0cdovl21hdhroyw1pbhrvbi5uzxqvyxn5bmmtc2vydmljzs1jywxscy1jb3jvdxrpbmvzp3jldmlzaw9upte=
No new comments are allowed on this post.
Comments
Craigology
Hi Matt,
Great series on coroutines in MVVM.
Wondering what's your approach, if any, for unit testing coroutines + async?
Where would you put your seams amongst this to facilitate testability?
Matt Hamilton
Hi Craig,
I haven't actually tried testing yet, but I think it should be a snap. Since your method returns an
IEnumerable<IActivity>
you can just call it and check the results. Something like:Obviously testing the activities themselves would be a separate process.