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

8
  • Session is null because AJAX is REST and REST doesn't implement sessions. Commented Oct 22, 2013 at 16:09
  • What is _searchProvider ? 3rd party? How this process? Commented Oct 22, 2013 at 16:09
  • @Chris, Session is alreadt null on Ready event. Anyway, what is the correct way to implement what i have described? Commented Oct 22, 2013 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. Commented Oct 22, 2013 at 16:11
  • Pretty sure AJAX requests include all browser cookies which includes the session variables. Commented Oct 22, 2013 at 16:12

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

6 Comments

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?
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.
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.
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?
@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.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.