Saturday, 7 December 2013

Using TPL to avoid callback hell

In this example, we fire off three service calls which individually have a continuation of converting the resultant DTO to a model object.

We then call a fourth service, to get the Spot which only fires once all the previous three service calls have completed and created their model object.

var tasks = new Task[]
.ContinueWith(t => MoneyMarketRate = Mapper.Map<DTOs.MoneyMarketRate, MoneyMarketRate>(t.Result)),

.ContinueWith(t => InvestmentBoundaries = Mapper.Map<DTOs.InvestmentBoundaries, InvestmentBoundaries>(t.Result)),

.ContinueWith(t => TradingDate = Mapper.Map<DTOs.TradingDate, TradingDate>(t.Result)),

ts => services.GetAsTask<DTOs.Spot>()
.ContinueWith(t => Spot = Mapper.Map<DTOs.Spot, Spot>(t.Result)));
You can take a look at my Silverlight source project here:
The problem with this code is that the exceptions are not properly handled.  For that we can leverage async and await:
var tasks = new []
services.GetAsync<DTOs.MoneyMarketRate, MoneyMarketRate>(x => MoneyMarketRate = x),
services.GetAsync<DTOs.InvestmentBoundaries, InvestmentBoundaries>(x => InvestmentBoundaries = x),
services.GetAsync<DTOs.TradingDate, TradingDate>(x => TradingDate = x),

await TaskEx.WhenAll(tasks);
await services.GetAsync<DTOs.Spot, Spot>(x => Spot = x);
catch (Exception e)

I also refactored the service executor to use async and await and move the setting to the property to an action that can be passed in.
public class ServiceExecutor
const string BaseAddress = "http://localhost:8080/Services/";

public async Task GetAsync<TDto, TModel>(Action<TModel> actionToSetPropertyValue)
var client = new WebClient();
var serviceName = typeof(TDto).Name + ".ashx";
var response = await client.DownloadStringTaskAsync(new Uri(BaseAddress + serviceName, UriKind.Absolute));
var dto = JsonConvert.DeserializeObject<TDto>(response);
actionToSetPropertyValue.Invoke(Mapper.Map<TDto, TModel>(dto));

Refactored source: