John
John

Reputation: 2202

ASP.NET MVC, ajax requests are not being processed asynchronously

I'm having a big application project that we've been working on for a while. I'm 90% sure that this is a new issue that actually worked before without problems.

When I call two ajax request the second one waits for the first to complete before processing. When I check the network traffic I can see that the client (browser) sends both requests to the server but it seems to be the server that is only serving one request at a time.

I can't figure out what this is. I found answers that says it could be the property enableSessionState that makes MVC serving requests synchronously so I looked into that, no help.

I started a new MVC 4 project and made an example code which works exactly as I want. Something must have been screwed up in our main project.

I will post some code. The point is that I want to start a procedure or something that takes a while to finish. And then read from a log to present progressing information to the user. But now the procedure starts and when it finishes the user gets all the information gathered at the same time at the end. But when it works, like in the new project, the user gets the information piece by piece while the slow procedure is still executing.

Does anyone have some thoughts that may help me? Pointing me in the right direction where to look more?

View:

@{
    ViewBag.Title = "Index";
}

<style>
    .calculation {
        overflow-y:scroll;
        width:400px;
        height:600px;
        background-color:#eee;
        border:solid 1px #000;
        padding:10px;
    }
</style>
<script src="~/Scripts/jquery-1.8.2.js"></script>
<h2>Index</h2>

<div class="calculation"></div>

<script>
    $(function () {
        $.ajax({
            type: 'POST',
            url: 'Calculation/DoSlowCalculation',
            contentType: 'application/json; charset=utf-8'
        });

        function readLog() {
            $.ajax({
                type: 'POST',
                url: 'Calculation/ReadCalculationLog',
                contentType: 'application/json; charset=utf-8',
                success: function (log) {
                    var completed = false,
                        div = $('.calculation');
                    for (var c1 = 0, l = log.length; c1 < l; c1++) {
                        div.prepend('<div>' + log[c1].LogText + '</div>');
                        if (log[c1].Complete) { completed = true; }
                    }
                    if (completed) {
                        alert('Calculation completed');
                    }
                    else {
                        setTimeout(function () {
                            readLog();
                        }, 1000);
                    }
                }
            });
        }

        readLog();
    });
</script>

Controller:

public class CalculationController : Controller
    {
        public ActionResult Index()
        {
            return View("Index");
        }

        public void DoSlowCalculation()
        {
            using (var conn = new SqlConnection("Server=.;Database=SlowCalculationDB;Trusted_Connection=True;"))
            {
                using (var cmd = new SqlCommand(@"
                    INSERT INTO Log
                    VALUES ('Starting calculation', GETDATE(), 0, 0);

                    WAITFOR DELAY '00:00:04'

                    INSERT INTO Log
                    VALUES ('Some more calculation...1', GETDATE(), 0, 0);

                    WAITFOR DELAY '00:00:04'

                    INSERT INTO Log
                    VALUES ('Some more calculation...2', GETDATE(), 0, 0);

                    WAITFOR DELAY '00:00:04'

                    INSERT INTO Log
                    VALUES ('Some more calculation...3', GETDATE(), 0, 0);

                    WAITFOR DELAY '00:00:04'

                    INSERT INTO Log
                    VALUES ('Some more calculation...4', GETDATE(), 0, 0);

                    WAITFOR DELAY '00:00:04'

                    INSERT INTO Log
                    VALUES ('Ends calculation', GETDATE(), 0, 1);
                ", conn))
                {
                    conn.Open();
                    cmd.ExecuteNonQuery();
                }
            }
        }

        public ActionResult ReadCalculationLog()
        {
            using (var conn = new SqlConnection("Server=.;Database=SlowCalculationDB;Trusted_Connection=True;"))
            {
                using (var cmd = new SqlCommand(@"
                    SELECT LogText, LogTime, Complete FROM Log WHERE [Read] = 0;
                    UPDATE Log SET [Read] = 1;
                ", conn))
                {
                    conn.Open();
                    var dt = new DataTable();
                    var sqlAdapter = new SqlDataAdapter(cmd);
                    sqlAdapter.Fill(dt);

                    var logs = from l in dt.AsEnumerable()
                               select new
                               {
                                   LogText = l.Field<string>("LogText"),
                                   LogTime = l.Field<DateTime>("LogTime").ToString("HH:mm:ss"),
                                   Complete = l.Field<bool>("Complete")
                               };

                    return Json(logs.ToList());
                }
            }
        }
    }

Upvotes: 1

Views: 605

Answers (1)

severin
severin

Reputation: 5493

MVC will process requests made by the same user synchronously, so when you enabled sessions you also enabled this.

To allow actions to be processed asynchronously you must annotate the controller with [SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)], like this:

[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
public class ParallelController : Controller
{
...
}

This will not work on an action level, so you might need to use a separate controller for the actions in question.

Upvotes: 3

Related Questions