Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Asynchronous Access to Long Running Web Service Tasks

0.00/5 (No votes)
27 Apr 2009 1  
Asynchronous access to long running Web Service tasks

Introduction 

Quite often we have to deal with slow and lengthy Web Services tasks and consuming such Web Services can be equally frustrating. One solution to overcome this problem is to access the Web Service in an asynchronous manner while running the slow and lengthy Web Service task itself asynchronously. The beauty of the solution is that it is non blocking asynchronous call and the UI remains unaffected. 

Using this method also helps you maintain and manage state in your Web Services.

Again this is probably just one possibility to many solutions in dealing with this kind of an issue.

Background  

I came across this issue while surfing the ASP.NET forum. I found it to be an extremely interesting problem simply because the solution could be as convoluted as this.

When the client that is consuming the slow and lengthy service initiates the Web Service call, the webmethod generates and returns a GUID. Using this as a key we create an entry in the HTTPRuntime Cache and spawn a background thread that runs the slow and lengthy task and when complete, will update the HttpRuntime Cache entry with the result. 

The client periodically polls the Web Service to check if the slow and lengthy task has completed.  

Using the Code    

The download zip includes a Web app client that incorporates an AJAX based polling system and a Web Service that runs the task in a background thread. 

Client Code  

The client initiates the web request by calling the startCalculate web method and storing the id it receives in a session object called key:

 protected void Button1_Click(object sender, EventArgs e)
 {
       Session["key"] = sv.startCalculate();
       Lab_Result.Text = "Retrieving the result for ID " + Session["key"].ToString() 
       + " ...";   
 } 

The client uses the AJAX timer control's Tick event to do the polling. It calls the  retrieveValue(string ID) by passing the ID stored in the session object called key.  If returned value is "Waiting" or contains "Retrieving" I continue polling for the result. Once the result is returned, it will no longer poll reducing on unnecessary calls to the Web Service.

 protected void Timer1_Tick(object sender, EventArgs e)
 {
        if (Session["key"] != null)
        {
              TmpVar = Lab_Result.Text;

              if ((TmpVar == "Waiting!") || (TmpVar.Contains("Retrieving")) || 
              (TmpVar == null))
              {
                  TmpVar = sv.retrieveValue(Session["key"].ToString());
              }

              Lab_Result.Text = TmpVar;
        }
 }  

Web Service Code

The following Webservice SlowServiceInMemory exposes two web methods startCalculate()  and retrieveValue(string ID)

The startCalculate() method generates an ID, which it returns to the consuming client. It creates a HTTPRuntime Cache entry using the ID and calls the calculate(string key) method that in turn spawns a background thread to complete the slow and lengthy task.

 [WebMethod]
        public string startCalculate()
        {
            string key = Guid.NewGuid().ToString();
            insertCacheObject(key, "Waiting!");
            calculate(key);
            return key;
        }   

The insertCacheObject method makes the Cache entry using the string key as an identifier.

 private void insertCacheObject(string key, string ver)
 {
            HttpRuntime.Cache.Insert(
               key
               , (object)ver
               , null
               , DateTime.Now.AddSeconds(15)
               , Cache.NoSlidingExpiration);
 }

The retrieveValue(string key) web method retrieves the result that is stored in the HttpRuntime.Cache entry using the key.

[WebMethod]
        public string retrieveValue(string key)
        {
            string tmp = "Waiting!";
            try
            {
                tmp = HttpRuntime.Cache.Get(key).ToString();
            }
            catch (Exception ex)
            {
                tmp = "Error " + ex.Message;
            }
            return tmp;
        }

The calculate(string key) method spawns a background thread to perform the slow and lengthy task. It then stores the result in the Cache entry.  You can further experiment  by changing the Thread.Sleep value to a smaller or larger number.

private void calculate(string key)
{
    Thread th = new Thread(
        delegate()
        {
            Thread.Sleep(10000);
            string Tmp = "Result for long running class!";
            insertCacheObject(key, Tmp);
        });
    th.IsBackground = true;
    th.Start();
}

Points of Interest 

You could extend this implementation to persist the result to a database or a flat file instead of the cache.    

History  

  • 27th April, 2009: Initial post

I would like to implement asynchronous calls to the Web Service using the IAsynchResult pattern. The IAsynchResult pattern supports a non blocking callback implementation for asynchronous calls to the Web Service. 

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here