Search code examples
ajaxasp.net-mvc-3sessionajax-request

MVC3, AJAX, Session and multipule request


Here is the scenario-

I have a form that uploads a file to a Action via Ajax POST. Once the file is uploaded it is processed and the data is inserted into a database. This can take some time so I would like to be able to send messages back to the user letting the user know at what step in the process the file is at. Basically what I have right now is:

[HttpPost]
public void UploadAndProcessFile()
{
   Session["UserMessage"] = "Start";
   // Do some stuff
   Session["UserMessage"] = "25% done";
   // Do some stuff
   Session["UserMessage"] = "50% done";
   // Do some stuff
   Session["UserMessage"] = "75% done";
   // Do some stuff
   Session["UserMessage"] = "Completed!";
}

[HttpGet]
putlic JsonResults GetFileProcessStatus()
{
  string returnMessage = (Session["UserMessage"] ?? "");
  return Json(returnMessage, JsonRequestBehavior.AllowGet);
}

On the client side I post the form via ajax to the UploadAndProcessFile Action and then have a function that continually does an ajax get request to GetFileProcessStatus. The problem is that when I do the ajax get reqeust to GetFileProcessStatus the Session["UserMessage"] is always null. I have also tried TempData instead of the Session with the same results. From what I understand about Session, what I am trying to accomplish here is not doable because the precedence for Session is given to the first caller. In my case the first caller would be UploadAndProcessFile. I hope this make sense and that someone can help!

Thanks - JD


Solution

  • class ParameterForMyProcess
        {
            public string criteria { get; set; }
            public Guid processID { get; set; }
            public HttpContext context { get; set; }
        }
    
        private Guid StartProcess(string criteria)
        {
            // we will use this id later to get status updates on this specific process.
            var processID = Guid.NewGuid();
    
            // the thread we are starting can only take one parameter,
            // so we create a container for all the data we might want to use
            // and pass that in as the single parameter into the process.
    
            var parameter = new ParameterForMyProcess()
                {
                    criteria = criteria,
                    processID = processID,
                    context = System.Web.HttpContext.Current
                };
    
            var thread = new System.Threading.Thread(
                new System.Threading.ParameterizedThreadStart(CreateProcess));
    
            thread.Start(parameter);
            return processID;
        }
    
    private void CreateProcess(object parameter)
        {
            // cast the object to our parameter type
            var myParameter = parameter as ParameterForMyProcess;
    
            // you now have access to some data if you wish
            var criteria = myParameter.criteria;
    
            // process ID to report progress with
            var processID = myParameter.processID;
    
            System.Web.HttpContext.Current = myParameter.context;
    
            // Do something
    
            updateStatus(processID, "Hello World");
         }
    
         private void updateStatus(Guid processID, string status)
        {
            // store the status in the http cache.  
    
            var key = "CreateProcess:" + processID.ToString();
            var cache = System.Web.HttpContext.Current.Cache;
            var oldStatus = cache[key];
            var newStatus = oldStatus + "<br/>" + status;
            cache[key] = newStatus;
    
            // this implementation relies on the browser to trigger an action which will clean up this data from the cache.
            // there is no guarantee this will be triggered (user closes browser, etc), 
            // but since the cache has means for eventually removing this data, this isn't a big risk.
        }
    
        public JsonResult GetProcessStatus(string processID)
        {
            var status = System.Web.HttpContext.Current.Cache["CreateProcess:" + processID.ToString()];
            return Json(new { status = status });
        }