Thursday, September 17, 2009

Creating an Asynchronous ASP.NET Page To Initiate Batch Processes

I recently created a page that gave the user the option of initiating a long-running batch process.  The batch required repeatedly calling external web services that would have taken far too long for a user to wait for the traditional page processing model.  But, by using ASP.NET asynchronous execution to initiate the batch process on a separate thread, the page response is excellent.  I started by working through this excellent tutorial on asynchronous execution, but quickly ran into problems when I began calling web services.  Creating an asynchronous process involves writing a method that can execute without needing anything from the HttpContext (Application, Page, Session, Cache, Configuration, etc.) since this is not available when you use BeginInvoke to run your subroutine.  This presented me with an issue since my project had a library that we used to setup web service instances (with WSE security), but that library relied on the HttpContext’s Application object.  To work around this limitation, I created a private inner class that encapsulated the web service references. 

    Private Class WebSvcInstances

        Public FSI As MyWseSVc1 = GetMyWseSVc1

        Public PSI As MyWseSVc2 = GetMyWseSVc2

    End Class

The inner class was passed into the batch method while the Application object was still available and providing the objects the batch would need later.

To start off, create the subroutine you want to initiate asynchronously:

    Private Sub InitiateBatch(ByVal wsRefs As WebSvcInstances)

Create a Delegate in your code behind with the same signature as the subroutine:

    Private Delegate Sub Batch_Delegate(ByVal wsRefs As WebSvcInstances)

Then you can initiate your subroutine in a page event like so:

    Dim batchDelegate As InitiateBatch_Delegate

    Dim wsRefs As New WebSvcInstances

    batchDelegate = New InitiateBatch_Delegate(AddressOf InitiateBatch)

    Dim asyncResult As IAsyncResult

    asyncResult = batchDelegate.BeginInvoke(wsRefs, Nothing, Nothing)

You can read up on BeginInvoke to learn more about the callback features.  I didn’t need to be notified when the execution finished, so I just set those to Nothing