0

Imaing we have a Controller with

1) An action which starts some long operation, writes something to a session and immediately renders some user-friendly message:

    public ActionResult Search(string query)
    {
        _searchProvider.Ready += SearchResultReady;
        _searchProvider.Request(query);
        Session["query"] = query;
        return Results();
    }
    private void SearchResultReady(IEnumerable<IObject> results)
    {
        Session["searchResult"] = results.ToList();
    }

When the 'Search' is done, the result is gets saved Session.

The reason we do this is to display results when they will be ready(to be requested with ajax)

public ViewResult Results()
{
    if (Session["searchResult"] == null)
        return View("Wait");

    var query = Session["query"] as string;
    var list = Session["searchResult"] as IList<IObject>;
    var model = new ResultModel(query, list);
    return View("Results", model);
}

Now the problem is that on Ready event, the Session is null.

What is the proper way to to save the Contoller's state between requests

Ilya Smagin
  • 5,992
  • 11
  • 40
  • 57
  • Session is null because AJAX is REST and REST doesn't implement sessions. – Chris Pratt Oct 22 '13 at 16:09
  • What is `_searchProvider` ? 3rd party? How this process? – Murali Murugesan Oct 22 '13 at 16:09
  • @Chris, Session is alreadt null on Ready event. Anyway, what is the correct way to implement what i have described? – Ilya Smagin Oct 22 '13 at 16:10
  • @ChrisPratt `AJAX is REST`? I still use ajax in mvc and the i could able to see the session cookie passed along with request, also i could access the `Session[key]` in controller action. – Murali Murugesan Oct 22 '13 at 16:11
  • Pretty sure AJAX requests include all browser cookies which includes the session variables. – Trevor Elliott Oct 22 '13 at 16:12
  • @Murali _searchProvider is some thing injected from the constuctor. It works slowly due to request to a huge database, which is processed on a separate thread, for example. – Ilya Smagin Oct 22 '13 at 16:12
  • possible duplicate of [Do asynchronous operations in ASP.NET MVC use a thread from ThreadPool on .NET 4](http://stackoverflow.com/questions/8743067/do-asynchronous-operations-in-asp-net-mvc-use-a-thread-from-threadpool-on-net-4) – Damien Oct 22 '13 at 16:13
  • @IlyaSmagin What is the nature of the operation being performed on the huge database? Is it only reading or also writing data? How long in actual seconds or minutes do you expect it to take? – Trevor Elliott Oct 22 '13 at 16:54

1 Answers1

1

You're not going to be able to use sessions for this. Sessions are not transmitted via AJAX, so the API endpoint you're hitting never gets a session token to look up. In fact, if you're dealing with a true REST API there's no such thing as a session in the first place. HTTP is a stateless protocol.

Additionally, if you do any work inside the the controller action, the response will not be returned until the result is actually ready, negating the need to fetch it later with AJAX. Even if you implement async (which you aren't even doing here), that merely releases the thread back to the server pool so that it can field other requests until the action finishes; it does not return the response faster.

If you want to load the page first and then fetch data from a long running task, you should simply render the page and let the API endpoint do the work once the page fires off a request for it via AJAX. Implement async on that so you don't deadlock the thread, and present some loading animation to the user while they wait for the AJAX request to complete.

UPDATE

Controller Action

public ActionResult Search(string query)
{
    return View();
    // that's right: do nothing here, just return the view
}

JavaScript for View

var interval;

$(document).ready(function () {
    var query = location.search.split('=')[1];
    $.post('/do/some/work', { query: query }, function (data) {
        // render data
        clearInterval(interval);
    });

    function checkStatus() {
        $.get('/check/on/status', function (data) {
            // data would contain percentage value or something,
            //use that to update progress bar
        });
    }

    interval = setInterval(checkStatus, 1000);
});

That's all quick and dirty. You should find a more robust way to get the search query. Maybe even set it with a view model in your action and then return that into your view or something. Also, JavaScript should use proper namespacing so some other script doesn't run over your interval variable, etc.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • Ok let me describe it this way. There's a huge db which user needs to make request to, and while it's being processed and the results are not yet ready, I want to show some message, and when ready - display it. I thought it would be good idea to search in other thread, but when search is done, record the info to the session. That time ajax will repeatedly check Results() and sooner or later the user will get them. Am I doing it THAT wrong? – Ilya Smagin Oct 22 '13 at 16:26
  • In short, yeah. "Async is hard" is a saying for a reason. It took me a while to get my head fully around it too. When your controller action receives the request, it will *not* return the response until all work is done in the action. Even if you create a new thread to do the work in, the action must stay alive to monitor that thread. If you make the action asynchronous (which you should), that still only lets the server use that thread to field other requests while the action is waiting to complete. – Chris Pratt Oct 22 '13 at 16:31
  • So, regardless, the client will *not* get a response until the work is done, anyways, so you might as well just return the result at that point with the response. To give some response to the user, you must just send back a response as normal from the action and then have the page fire off an AJAX request once it loads. You do not need to long poll unless you just want to check the status of the work being done (to get a percentage of something), otherwise, that AJAX request will only return once the work is done. – Chris Pratt Oct 22 '13 at 16:32
  • Yes, but how to do this? How to return to the client something small, make his javascript tick and check readyness every second and then post in back? – Ilya Smagin Oct 22 '13 at 16:34
  • @ChrisPratt AJAX transmits session cookies just fine. You may be confusing ASP.NET Web Api limitations with AJAX limitations. REST services *should* be stateless and shouldn't implement session saving which is why Web Api is not configured to support the Session states by default (although you can hack it to do so). Regardless, in this case he's not using ASP.NET Web Api he's only using an MVC controller. So he is free to use session variables with AJAX requests. – Trevor Elliott Oct 22 '13 at 16:42
  • Regardless, you are right about the solution to his problem. He should absolutely use a long AJAX request and simply wait for the data to be returned as JSON for example. Implementing an async controller is an optional step once everything is already working since it's just a performance micro-optimization meant to deal with heavy loads and scarcity of thread pool threads. – Trevor Elliott Oct 22 '13 at 16:47