Creating Responsive Portals - Async Tasks
Categories
Web portals are the ultimate companion to that cool SOA architecture you've been working on. With all of those great line-of-business (LOB) services that you've created, web parts seem like the ideal way of allowing your users to interact with that data.
Getting started with this is real easy. You write 20-30 lines of code and whoosh!... your LOB data is right up there on your portal.
protected override void OnPreRender(EventArgs e) {
base.OnPreRender(e);
WeatherType weather = GetWeather();
img1.ImageUrl = string.Format("~/images/{0}.gif", weather.ToString());
}
In this piece of code we can see that in the PreRender code of our web part that we make our service call - GetWeather() - and use the data that is returned from that call to display a relevant image to the end user. Pretty typical code really. As part of the processing of the portal page by ASP.NET, your web part will get called upon to render itself.

Of course, as time goes by, you will undoubtedly add many new web parts to your portal page: Calendars, Weather, CRM data, Reports, etc. And each of these will trundle off to your SOA services, get their data, and come back with their own view for your users to use. Of course there's a problem here. Because of the synchronous nature of the ASP.NET web page where controls are loaded and executed in sequence, each control adds time to the overall processing length of the page:

So now you have a page which - with all of those calls to external resources - is taking ages to load... maybe 10-20 seconds for a page with only a few web parts on it. What to do?
Given that each of these web parts are independent of each other we'd ideally like to run them in parallel so that we don't wear the large time penalty for the whole page. Something like this:

In ASP.NET 2.0 they actually added support for easily creating pages which allow tasks to be run asynchronously in a manner similar to this. All that you have to do to gain these benefits are to include an Async="true" attribute to your Page directive so that ASP.NET knows to implement the IHttpAsyncHandler in the page, and then write your code so that it executes within asynchronous task blocks. There's an excellent article by Fritz Onion which discusses this architecture here:
http://msdn.microsoft.com/msdnmag/issues/06/07/ExtremeASPNET/
In the article, Fritz highlights the fact that we can simply use the existing asynchronous interfaces that come with things like ADO.NET and Web Service generated proxies in .NET and plug them into the architecture. When you need to execute asynchronous tasks for which no asynchronous interfaces already exist - such as reading from the file system - then you will need to queue your own delegate to to that work:
private delegate WeatherType DoStuffDelegate();
IAsyncResult BeginGetWeather(object src, EventArgs e, AsyncCallback cb, object state) {
DoStuffDelegate d = new DoStuffDelegate(GetWeather);
return d.BeginInvoke(cb, state);
}
void EndGetWeather(IAsyncResult ar) {
DoStuffDelegate d = (DoStuffDelegate)((AsyncResult)ar).AsyncDelegate;
WeatherType weather = d.EndInvoke(ar);
img1.ImageUrl = string.Format("~/images/{0}.gif", weather.ToString());
}
Notice that in this code, our expensive GetWeather() operation is queued to run asynchronously. The final thing is to show the pattern that you should implement to ensure that your web parts are ready to take advantage of a hosting page that is set to Async="true":
if (Page.IsAsync) {
Page.RegisterAsyncTask(
new PageAsyncTask(
new BeginEventHandler(BeginGetWeather),
new EndEventHandler(EndGetWeather),
null, null, true)
);
} else {
WeatherType weather = GetWeather();
img1.ImageUrl = string.Format("~/images/{0}.gif", weather.ToString());
}
Now our web part will always work, but will allow our users to gain the benefits of asynchronous processing when it is enabled at Page level.