With C# and Async integration in the language, you’ll wonder how this fits in your current application infrastructure. I have started porting an old application into Silverlight 5.0 which comes with new C# 5.0 and Async features and I needed a way to integrate this with the Calibrun framework I had in place.

Caliburn Async Abstractions

In Caliburn you could expose your ViewModel actions as IResult / IEnumerable<IResult>. I have blogged about this before, but in short, it is a good way to abstract away the whole async stuff you have in your ViewModel and it made testing async actions in the view model much easier. Here’s a typical action I had on a ViewModel:

1
2
3
4
5
6
7
8
public IEnumerable<IResult> LoadEmployee()
{
yield return Show.Busy();

yield return new ServiceResult().Invoke(new EmployeeServiceClient().GetEmployeeAsync, SelectedEmployee.Id);

yield return Show.NotBusy();
}

By using IResult we have modified the whole async and continuation into coroutines which is much cleaner to read and easier to follow.

Hello Async

Now with async stuff in the horizon, how would async / await fit this scenario? Let’s suppose we have the following async method in our View Model:

1
2
3
4
5
6
private void LoadEmployeeAsync()
{
var service = new EmployeeServiceClient();
service.GetEmployeeCompleted += (s, e) => { CurrentEmployee = e.Result };
var employee = service.GetEmployee(SelectedEmployee.Id);
}

At first we should convert this service call into the new async pattern:

1
2
3
4
5
6
private async Task<Employee> LoadEmployeeAsync()
{
IEmployeeSevice service = new EmployeeServiceClient();

return await Task.Factory.FromAsync<Employee>(client.BeginGetEmployee(SelectedEmployee.Id, null, null), ar => client.EndGetEmployee(ar));
}

A couple of things to notice: We are now using the interface of the generated service, which exposes IAsyncResult and we’re returning the method call with a Task<TResult>. With the new async pattern, your method should either return void or Task / Task<T> before you can use the async keyword.

There’s an IResult for that

Now the question is how to call the async method in a sequence of IResults. This would be the first attempt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public IEnumerable<IResult> LoadEmployee()
{
yield return Show.Busy();

yield return new ActionResult(() => LoadEmployeeAsync());

yield return Show.NotBusy();
}

public class ActionResult : IResult
{
public ActionResult(Action action)
{
ToExecute = action;
}

public Action ToExecute { get; set; }

public override void Execute(ResultExecutionContext context)
{
ToExecute.Invoke();
RaiseCompleted();
}
}

Apparently this won’t work as expected because as soon as we call the async method it will return and we’ll fire the Completed event on IResult which will move to execution of the next IResult in the sequence. What we want to do is to wait for the completion of the Task and then raise the Completed event, so I came up with this implementation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class TaskResult : IResult
{
public TaskResult(Func<Task> task)
{
ToExecute = task;
}

public Func<Task> ToExecute { get; set; }

public override void Execute(ResultExecutionContext context)
{
ToExecute.Invoke().ContinueWith(task =>
{
Execute.OnUIThread(() => RaiseCompleted(task.IsCanceled, task.Exception));
});
}
}

This is just the same API that is available today on TPL, so nothing fancy, the only thing worth mentioning here is that we’re raising the Completed event on the UI thread using Execute class of Caliburn. The Execute class uses IDispatcher interface that wraps Silverlight / WPF dispatcher object. The final version of the TaskResult can be used like this:

1
2
3
4
5
6
7
8
public IEnumerable<IResult> LoadEmployee()
{
yield return Show.Busy();

yield return new TaskResult<Employee>().Invoke(LoadEmployeeAsync).ContinueWith(task => CurrentEmployee = task.Result);

yield return Show.NotBusy();
}

This now fully supports calling Task or Task<T> methods and also has its own continuation support. This works right alongside other IResult implementations. Ideas?