asp.netvb.netasynchronouswebformsfire-and-forget

Send Email without waiting for the action to finish execution after Button click ASP.NET


In short, remaining in the HTTP context, I would like the user, after clicking on an order completion button, not to wait for the mails to be sent before being sent back to a "thak you page".

I saw that HostingEnvironment.QueueBackgroundWorkItem could help me with this but there is always the risk of it being killed by IIS recycling.

What is the best solution to do this?

I know the best would be to develop a separate console solution but it wouldn't be worth it for 3/4 emails, alternatively I could consider speeding it up by making them asynchronous?

Protected Sub btnConcludiOrdine_Click(sender As Object, e As System.EventArgs) Handles btnConcludiOrdine.Click
    If IsValidOrder(Me._cart, msg) Then
      If Me._cart.SaveOrder(Me._user, Me._orderCode, Me._lang) then
           'Update quantity in db
           Dim mail As New EmailBLL
           mail.SendOrderNotice(Me._cart, Me._lang) '2 Mails
           mail.SendProductNotice() '2 Mails
      End If
    Else
      Response.Redirect("*Error URL*")
    End If
End Sub

Solution

  • The way you approach this is as suggested – start a task, or so called new processor thread.

    So, what you would do is break out the code – this even works if code behind is for a web form.

    So, the first step is to move out the “slow” parts, or the parts we want to run separate.

    The main issue is that to start/launch/want/desire/achieve a brand new processor thread?

    The sub call CAN ONLY PASS ONE “parameter” and the sub can only accept one parameter!!!!!

    I note in your case that routine needs two values.

    However, that “one parameter” can be a array of “many” values, or even a collection or whatever. In our case we pass the two values.

    So just keep in mind that what you call can NOT update or “use” the values of controls on the form – the instance of that form will go out of scope.

    But we can of course PASS the values you need. This will allow that routine to run 100% independent of the web form.

    I also VERY strong suggest that if you DO place the sub in the same web page code behind? You should/can mark that sub as shared. Doing so will allow the compiler to get mad at you and spit out errors if that routine say tries to use or update a control value on the form.

    However, it is MUCH better is to place this sub in a separate standard code module out side of the web forms code behind.

    Regardless of above, we can now re-write the code we have as this:

    If Me._cart.SaveOrder(Me._user, Me._orderCode, Me._lang) then
           
           Dim myInfo(1) as object
           myInfo(0) = me.cart
           myInfo(1) = me_._lng
    
           Call MyUpdateQ(myInfo) 
      End If
      ' bla bla lba
    
    
    Shared Sub MyUPdateQ(p() as object)
    
           'Update quantity in db
           Dim mail As New EmailBLL
    
           mail.SendOrderNotice(p(0),p(1) 
           mail.SendProductNotice() '2 Mails
    
    End Sub
    

    Ok, so far, we not achieved much, but we re-writing to accept the ONE array is KEY here.

    So, now now make sure the above runs/works and is all happy.

    Now, because we moved out the "work load" to that one routine, it is now a simple matter to start a thread.

    Now, Our above code becomes this:

    Protected Sub btnConcludiOrdine_Click(sender As Object, e As System.EventArgs) Handles btnConcludiOrdine.Click
    
       If IsValidOrder(Me._cart, msg) Then
          If Me._cart.SaveOrder(Me._user, Me._orderCode, Me._lang) then
             Dim myInfo(1) as object
             myInfo(0) = me.cart
             myInfo(1) = me_._lng
    
             Dim MyThread As New Thread(New ParameterizedThreadStart(AddressOf MyUpdateQ))
    
             MyThread.Start(myInfo)
          End If
       Else
          Response.Redirect("*Error URL*")
       End If
    End Sub
    
    Shared Sub MyUPdateQ(p() as object)
    
           'Update quantity in db
           Dim mail As New EmailBLL
    
           mail.SendOrderNotice(p(0),p(1) 
           mail.SendProductNotice() '2 Mails
    
    End Sub
    

    That is it. Now when you click your button it will wait ZERO time, since the long running routine is now going to run 100% as a separate thread. And this will also mean that when the user clicks the button - the page will respond instant and post back to user will be done. So if that thread takes 6 seconds, or even 25 seconds, the user will not notice this delay.