Search code examples
asynchronouswcfiisbackground-processapplication-pool

WCF: Method return a response, after spawning a thread to do background processing


So I have the following scenario. I have a method in my WCF, where the client will send a request, the WCF service would then perform some background processing and do call an external webservice method, and the method will respond with an acknowledgement immediately (before the background processing has been completed).

The way I have thought of doing is having my WCF method return a response after spawning a thread to do the background processing, and call the external webservice. The flow is something like this:

  • Caller sends request to INITIAL_CALL

  • WCF starts a thread that calls PROCESS

  • WCF returns true

  • PROCESS makes call to EXTERNALWS and gets response in postResponse

  • postReponse gets logged to the database

See example code below:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class Service : IService


public bool INITIAL_CALL()
{

     new Thread(()=>
                {
                    PROCESS();
                }).Start();
    return true;
}


private void PROCESS()
{
    //Do some background processing and create request for call below
    var processRequest = "Request goes here";
    using (var client = new EXTERNALWS.ResponseTypeClient())
    {
       var postResponse = client.POST(processRequest);

       //Log postResponse to database

    }

}

Having in mind that PROCESS() may run for a long time, I just wanted to see if there is a better way of doing this with WCF and IIS? Or if there are any pitfalls that I have to consider i.e IIS app pool recycling destroying the thread.


Solution

  • I have found a solution for this. I ended up using Hangfire to do the background processing needed (https://www.hangfire.io/). Hangfire seems to be specifically made for this. I have implemented it following the documentation found at their homepage, in a separate ASP MVC application. I have also configured it as always running on IIS. All instructions and sample codes to setup Hangfire to do this are found here https://docs.hangfire.io/en/latest/index.html. I had to change the flow (since I am not spawning any new threads manually as previously), and also create a new table in the database so that the INITIAL_CALL in the WCF Application would queue all the long running jobs (later to be picked up and executed by Hangfire). Have in mind this is seperate from Hangfire's queue, this table will be checked by Hangfire in a predefined interval, and will check this database table that stores which function to call, its parameters, and an indicator if the job has already been picked up by Hangfire or not (to avoid the re-entrant scenario described here https://docs.hangfire.io/en/latest/best-practices.html). A little extra work, but works like a charm.

    The way the flow works now is as follows:

    • Caller sends request to INITIAL_CALL

    • In INITIAL_CALL, an entry is made in a new database table (this is the job queue that will be checked by Hangfire in a predefined interval).

    • INITIAL_CALL returns true

    • Hangfire checks this database table in a predefined interval using PROCESS_JOBS (this interval can be defined in Hangfire itself).

    • If there is a queued item, PROCESS_JOBS proceeds and makes the call to EXTERNALWS and gets response in postResponse. If not, it just exits and does nothing further.

    • postReponse gets logged to the database.

    See updated example code below:

    WCF Application

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    public class Service : IService
    
    
    public bool INITIAL_CALL()
    {
       //Add job queue entry in database table to be picked up by Hangfire 
        return true;
    }
    

    Hangfire Application

    public void PROCESS_JOBS()
    {
        //Check in a predefined interval if there is a pending job in the queue. 
        //If there is continue with below, otherwise exit function.
    
        //Do some background processing and create request for call below
        var processRequest = "Request goes here";
        using (var client = new EXTERNALWS.ResponseTypeClient())
        {
           var postResponse = client.POST(processRequest);
    
           //Log postResponse to database
    
        }
    
    }