Neeraj Kumar Gupta
Neeraj Kumar Gupta

Reputation: 2363

How to send multiple acknowledgement to client using IHttpAsyncHandler in asp.net

I have implemented IHttpAsyncHandler in my class to perform 5-6 long running process in background and acknowledge to client on start of each task.

Earlier I was using one session variable and updating it with current status of task, and giving async call request to server from jquery in interval of 5 seconds to get current status, but this implementation is not good because its continually hitting request to server for status.

Then I implemented IHttpAsyncHandler in my application, now server itself send acknowledgement to client, but as per my implementation I am able to send only one acknowledgement! if I try to send more than one then its giving error as "object reference not set to an instance of an object"

please check my sample code.

in my code ExecuteFirst() method works fine sending acknowledgement to client but ExecuteSecond() does not send acknowledgement its giving error.

I goggled a lot but not getting proper way to send multiple acknowledgement to client.

this is my sample code, please help me if any one have any idea.

Javascript

function postRequest(url) {

        var url = 'StartLongRunningProcess.ashx?JOBCODE=18'

        var xmlhttp = getRequestObject();

        xmlhttp.open("POST", url, true);


        xmlhttp.onreadystatechange =
                    function () {

                        if (xmlhttp.readyState == 4) {
                            var response = xmlhttp.responseText;
                            divResponse.innerHTML += "<p>" + response + "</p>";                             
                        }
                    }


        xmlhttp.send();

    }



 function getRequestObject() {
        var req;

        if (window.XMLHttpRequest && !(window.ActiveXObject)) {
            try {
                req = new XMLHttpRequest();
            }
            catch (e) {
                req = false;
            }
        }
        else if (window.ActiveXObject) {
            try {
                req = new ActiveXObject('Msxml2.XMLHTTP');
            }
            catch (e) {
                try {
                    req = new ActiveXObject('Microsoft.XMLHTTP');
                }
                catch (e) {
                    req = false;
                }
            }
        }

        return req;
    }

StartLongRunningProcess.ashx.cs

public class StartLongRunningProcess: IHttpAsyncHandler, IRequiresSessionState
{

            private AsyncRequestResult _asyncResult;
    public void ProcessRequest(HttpContext context) {}

    public bool IsReusable
    {
        get { return true;}
    }


    public IAsyncResult BeginProcessRequest(HttpContext context, System.AsyncCallback cb, object extraData)
    {

        int32 jobCode= convert.ToInt32(context.Request["JOBCODE"]);
        _asyncResult = new AsyncRequestResult(context, cb, jobCode);

                  if(jobCode==18)
                  {

                   StartProcess18()

                  }
                  else
                  {

                   //StartProcessOther()

                  }
}



            private StartProcess18()
            {

                var task1= new Task(() =>
                {
                     ExecuteFirst();                           
                });


                var task2 = task1.ContinueWith((t1) =>
                {
                    ExecuteSecond();
                }, TaskContinuationOptions.OnlyOnRanToCompletion);

                task1.Start();
            }



    private ExecuteFirst()
    {

          //notify to client that this job has been started
         _asyncResult.CurrentContext.Response.Write("First task has been started"); 
         _asyncResult.Notify();

         // Above line of code successfully send a acknowledgement to client 

         //Doing some long running process
    }


    private ExecuteSecond()
    {

          //notify to client that this job has been started
         _asyncResult.CurrentContext.Response.Write("Second task has been started"); 

          // Above line of code giving error and if I skip it and call Notify() this also does not work.

         _asyncResult.Notify();


         //Doing some long running process
    }

    public void EndProcessRequest(IAsyncResult result)
{

}


 }

AsyncRequestResult.cs

public class AsyncRequestResult : IAsyncResult 
{
    private HttpContext context;
    private AsyncCallback callback;     

    private ManualResetEvent completeEvent = null;
    private object data;
    private object objLock = new object();
    private bool isComplete = false;        

    public AsyncRequestResult(HttpContext ctx, AsyncCallback cb, object d)
    {
        this.context = ctx;
        this.callback = cb;
        this.data = d;
    }

    public HttpContext Context
    {
        get { return this.context; }
    }

    public void Notify()
    {
        //isComplete = true;


        lock(objLock)
        {
            if(completeEvent != null)
            {
                completeEvent.Set();
            }
        }


        if (callback != null)
        {
            callback(this);
        }
    }


    public object AsyncState
    {
        get { return this.data; }
    }


    public bool CompletedSynchronously
    {
        get { return false; }
    }


    public WaitHandle AsyncWaitHandle
    {
        get
        {
            lock(objLock)
            {
                if (completeEvent == null)
                    completeEvent = new ManualResetEvent(false);

                return completeEvent;
            }
        }
    }


    public bool IsCompleted
    {
        get { return this.isComplete; }
    }

}

Upvotes: 3

Views: 1096

Answers (1)

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149598

HttpContext.Current is not null only if you access it in a thread that handles incoming requests.

Your continuation task that is running is most likely running on a ThreadPool thread without the HttpContext.Current flowing to the continuation, hence it being null.

Try setting your TaskScheduler to TaskScheduler.FromCurrentSynchronizationContext() in order to execute it in the same context.

        private StartProcess18()
        {

            var task1= new Task(() =>
            {
                 ExecuteFirst();                           
            });


            var task2 = task1.ContinueWith((t1) =>
            {
                ExecuteSecond();
            }, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.FromCurrentSynchronizationContext());

            task1.Start();
        }

Upvotes: 1

Related Questions