Long Waited Task in Asp.net

Yesterday, one my my friend ask me a query about sending some 500+ emails using an asp.net page. Sending bulk email using asp.net is obviously an issue. I mean, You cannot keep the page on the post back state for the five minutes. Your page will get expired and even if you increase the Request Timeout period it is still not a good approach.

So for that, There are couple of approaches like
1. You can have a table in which you store all the emails and create a Windows Service or Schedule Task to read tables of send emails accordingly.
2. Open a thread on the server side using delegate and return the page to the browser and show the progress using Asynchronous operation. 

As We were operating on shared hosting first approach doesn't seem fruitful for us. So, we decided to go with the second approach and for that we use delegates. Let me show you step by step.

   1: Public Delegate Sub LongTimeTask_Delegate()
   2: Private fileName as String 

Here I have declared a delegate and a global variable called filename which I will use for getting the status of the process.
Now on the click event of my button which start the process. I have write the following code

   1: Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
   2:     Dim rnd As Double = New Random().Next()
   3:     fileName = Session.SessionID & "_"  & rnd & ".txt"
   4:     Dim d As LongTimeTask_Delegate
   5:     d = New LongTimeTask_Delegate(AddressOf LongTimeTask)
   6:  
   7:     Dim R As IAsyncResult
   8:     
   9:     R = d.BeginInvoke(New AsyncCallback(AddressOf TaskCompleted),nothing)
  10:     Dim strScript As String = "<script language='javascript'> setInterval('GetResult(""" + fileName + """)',2000); </script>"
  11:  
  12:     Button1.Enabled= False
  13:     Response.Write(strScript) 'TODO : Can take write on literal
  14: End Sub

On line no 3, I am setting filename variable to the session id and a random number
On line no 4, Declare and Initialize Delegate and passed a function that will be executed by Delegate
On line no 7 and on, We invoke the delegate and store the result in IAsyncResult and also specify the function that will execute once the delegate is complete.
On line no 10, we are writing a script on the page which is there to show us the status of process timely.

Now, lets come LongTimeTask function, in which we have the whole stuff.

   1: Public Sub LongTimeTask()
   2:     Dim totCount As Integer = 500
   3:     For a As Integer = 1 to totCount
   4:         Try
   5:             If Not File.Exists(fileName)
   6:                 File.Create(fileName)
   7:             End If
   8:             using sw As StreamWriter = New StreamWriter(Server.MapPath(fileName))
   9:             sw.WriteLine("Processing " + a.ToString() + " of " + totCount.ToString() + " emails")
  10:         End Using
  11:         Catch 
  12:  
  13:         End Try
  14:         System.Threading.Thread.Sleep(2000)
  15:     Next
  16: End Sub


Notice that, system will be in the loop for 500 times and each time before start processing it will update the status which will display to the browser. The Try/Catch block is updating the status where as notice the line no 14 in which I pause the thread. You can add your email sending or other long process stuff here but for me it is enough to sleep the thread for twenty seconds.

Let me show you what happen when the task gets finished.

   1: Public Sub TaskCompleted(ByVal R As IAsyncResult)
   2:     Try
   3:         using sw As StreamWriter = New StreamWriter(Server.MapPath(fileName))
   4:             sw.WriteLine("Process has been completed successfully <a href='default.aspx'>Click here </a> to go back") 'You might shoot an email or some thing else here
   5:         End Using
   6:         If File.Exists(fileName)
   7:                 File.Delete(fileName)
   8:         End If
   9:         Catch 
  10:  
  11:         End Try
  12: End Sub


This time, we have updated the status with some other text and a hyper link. You can also have any kind of alert here. This is just the part of the code behind stuff. ASPX file contain the actual Asynchronous logic here is the HTML

   1: <form id="form1" runat="server">
   2: <div>
   3:  
   4:     <asp:Button ID="Button1" runat="server" Text="Start IAsync" />
   5:     <div id="resultDiv"></div>
   6: </div>
   7: </form>

The resultDiv is responsible of showing the status. Now to get the status following Javscript will do the complete magic

   1: <script language="javascript">
   2:     var request = false;
   3:        try {
   4:          request = new XMLHttpRequest();
   5:        } catch (trymicrosoft) {
   6:          try {
   7:            request = new ActiveXObject("Msxml2.XMLHTTP");
   8:          } catch (othermicrosoft) {
   9:            try {
  10:              request = new ActiveXObject("Microsoft.XMLHTTP");
  11:            } catch (failed) {
  12:              request = false;
  13:            }  
  14:          }
  15:        }
  16:        
  17:       function GetResult(filename) {
  18:         var rnd = Math.random() * 1000000;
  19:         var url = filename + '?rnd=' + rnd;
  20:         request.open("GET", url, true);
  21:         request.onreadystatechange = GetResultComplete;
  22:         request.send(null);
  23:     }
  24:     function GetResultComplete() {
  25:         if (request.readyState == 4) {
  26:             
  27:             if (request.status == 200) {
  28:                 var divComm = document.getElementById('resultDiv');
  29:                 if (divComm) {
  30:                     divComm.innerHTML = request.responseText;
  31:                 }
  32:             }
  33:         }
  34:     }
  35:     </script>

There it is ... we are all done. Following is the snapshot of the working long waited task in asp.net

async
You can find the complete code for Visual Studio 2008 below ... cheers

1 Comment

  • Instead of using XMLHttpRequest function, you should use PageMethods. &nbsp;Be carefull, PageMethods cannot be called on MasterPages. In this case, you can use Script WebService.

Comments have been disabled for this content.