AsyncController v/s SessionLess Controller

    Introduction:

          AsyncController is introduced in ASP.NET MVC 2 while SessionLess controller is introduced in ASP.NET MVC 3. AsyncController allows you to perform long running I/O operation(s) without making your thread idle(i.e., waiting for I/O operations to complete). On the other hand, SessionLess controller allows you to execute multiple requests simultaneously for single user, which otherwise execute multiple requests sequentially due to session synchronization. Understanding these concepts may be easy for you but I have seen a lot of guys become confused on these concepts. In this article, I will show you how to use AsyncController and SessionLess controller in ASP.NET MVC application. I will also compare them and tell you what to use when, where, and the why.

 

    Description:

          The easiest way to understand these concepts is by example. Suppose you have a view which shows data from three different web services. The first web service retrieve the latest weather forecast of Karachi(the city where I live). Second web service find the longitude and latitude of Karachi. Third web service get the latest news about Karachi. The synchronous(normal) way to solve this problem is given below,

 

        public ActionResult SynchronousAction()
        {
            GetWeather();
            GetLatitudeAndLongitude();
            GetNews();
            return View();
        }

        private void GetWeather()
        {
            WebRequest webRequest = WebRequest.Create("http://www.google.com/ig/api?weather=Karachi");
            var webResponse = webRequest.GetResponse();
            StreamReader stream = new StreamReader(webResponse.GetResponseStream());
            string xml = stream.ReadToEnd();
            XDocument doc = XDocument.Parse(xml);
            var current_conditions = doc.Descendants("weather").Descendants("current_conditions").First();
            ViewData["condition"] = current_conditions.Element("condition").Attribute("data").Value;
            ViewData["temp_f"] = current_conditions.Element("temp_f").Attribute("data").Value;
            ViewData["temp_c"] = current_conditions.Element("temp_c").Attribute("data").Value;
            ViewData["humidity"] = current_conditions.Element("humidity").Attribute("data").Value;
        }

        private void GetLatitudeAndLongitude()
        {
            WebRequest webRequest = WebRequest.Create("http://where.yahooapis.com/geocode?q=Karachi");
            var webResponse = webRequest.GetResponse();
            StreamReader stream = new StreamReader(webResponse.GetResponseStream());
            string xml = stream.ReadToEnd();
            XDocument doc = XDocument.Parse(xml);
            var result = doc.Descendants("ResultSet").Descendants("Result").First();
            ViewData["latitude"] = result.Element("latitude").Value;
            ViewData["longitude"] = result.Element("longitude").Value;
        }

        private void GetNews()
        {
            WebRequest webRequest = WebRequest.Create("http://search.yahooapis.com/NewsSearchService/V1/newsSearch?query=karachi&results=1&appid=yahoodemo");
            var webResponse = webRequest.GetResponse();
            StreamReader stream = new StreamReader(webResponse.GetResponseStream());
            string xml = stream.ReadToEnd();
            XDocument doc = XDocument.Parse(xml);
            var result = doc.Descendants().First().Descendants().First();
            ViewData["NewsTitle"] = result.Elements().First().Value;
        }

 

           and here is the SynchronousAction view,

 

        @{
            ViewBag.Title = ViewData["Title"];
            Layout = "~/Views/Shared/_Layout.cshtml";
        }

        <h2>@ViewData["Title"]</h2>
        <h3>Weather Information</h3>
        <b>Condition:</b> @ViewData["condition"]<br />
        <b>Temperature in Fahrenheit:</b> @ViewData["temp_f"]<br />
        <b>Temperature in Celsius:</b> @ViewData["temp_c"]<br />
        <b>Humidity:</b> @ViewData["humidity"]<br /><br />

        <h3>Location Information</h3>
        <b>Latitude:</b> @ViewData["latitude"]<br />
        <b>Longitude:</b> @ViewData["longitude"]<br /><br />

        <h3>News Information</h3>
        <b>News HeadLine:</b> @ViewData["NewsTitle"]

 

           For the sake of simplicity, I am writing all the code inside the controller. Now just run this application. You will get all the information your application need. But there are some problems with this approach. The first problem is that your I/O request methods(GetWeather, GetLatitudeAndLongitude and GetNews) will execute sequentially. This may be not a big problem if I/O request methods will return within 1 or 2 seconds. But if GetWeather method takes 5 seconds to return then GetLatitudeAndLongitude and GetNews methods have to wait at least 5 seconds before they are allowed to execute. Next if GetLatitudeAndLongitude method takes 5 seconds to return then GetNews method have to wait at least 10 seconds before GetNews method allowed to execute. Depending on how much time your each I/O request method takes, this approach may be not optimum for your application.

           One way to overcome this problem is to spawn new threads for each I/O request method using ThreadPool.QueueUserWorkItem, Delegate BeginInvoke, Task, etc, methods. This will make your each I/O request method run concurrently. But these methods draw threads from the same thread pool that ASP.NET uses to service requests. By default, this thread pool has limited threads. So using thread pool threads to overcome the above problem can negatively impact your overall application performance. Another thing that may come into your mind is to use custom threads using Thread class. This will add another big overhead in your application called thread management(which is not an issue in thread pool case).  

           Second problem with the above approach is that your asp.net request servicing thread become idle for a certain time period, each time when your I/O request methods execute webRequest.GetResponse method. This time period will depend on the remote web service. If this idle time period is long then it will adversely affect your overall application performance because in this time period this valuable request servicing thread will do nothing but wait. So for utilizing this idle time period, you can use AsyncController. The asynchronous way to solve this problem is given below,

 

        public void AsynchronousActionAsync()
        {
            AsyncManager.OutstandingOperations.Increment(3);
            GetWeatherAsync();
            GetLatitudeAndLongitudeAsync();
            GetNewsAsync();
        }

        private readonly Object _lock = new Object();
        private void GetWeatherAsync()
        {
            WebRequest webRequest = WebRequest.Create("http://www.google.com/ig/api?weather=Karachi");
            webRequest.BeginGetResponse(result =>
                {
                    var webResponse = webRequest.EndGetResponse(result);
                    StreamReader stream = new StreamReader(webResponse.GetResponseStream());
                    string xml = stream.ReadToEnd();
                    XDocument doc = XDocument.Parse(xml);
                    var current_conditions = doc.Descendants("weather").Descendants("current_conditions").First();
                    lock (_lock)
                    {
                        AsyncManager.Parameters["condition"] = current_conditions.Element("condition").Attribute("data").Value;
                        AsyncManager.Parameters["temp_f"] = current_conditions.Element("temp_f").Attribute("data").Value;
                        AsyncManager.Parameters["temp_c"] = current_conditions.Element("temp_c").Attribute("data").Value;
                        AsyncManager.Parameters["humidity"] = current_conditions.Element("humidity").Attribute("data").Value;                        
                    }
                    AsyncManager.OutstandingOperations.Decrement();
                }, null);
        }

        private void GetLatitudeAndLongitudeAsync()
        {
            WebRequest webRequest = WebRequest.Create("http://where.yahooapis.com/geocode?q=Karachi");
            webRequest.BeginGetResponse(result =>
                {
                    var webResponse = webRequest.EndGetResponse(result);
                    StreamReader stream = new StreamReader(webResponse.GetResponseStream());
                    string xml = stream.ReadToEnd();
                    XDocument doc = XDocument.Parse(xml);
                    var result1 = doc.Descendants("ResultSet").Descendants("Result").First();
                    lock (_lock)
                    {
                        AsyncManager.Parameters["latitude"] = result1.Element("latitude").Value;
                        AsyncManager.Parameters["longitude"] = result1.Element("longitude").Value;
                    }
                    AsyncManager.OutstandingOperations.Decrement();
                }, null);
        }

        private void GetNewsAsync()
        {
            WebRequest webRequest = WebRequest.Create("http://search.yahooapis.com/NewsSearchService/V1/newsSearch?query=karachi&results=1&appid=yahoodemo");
            webRequest.BeginGetResponse(result =>
            {
                var webResponse = webRequest.EndGetResponse(result);
                StreamReader stream = new StreamReader(webResponse.GetResponseStream());
                string xml = stream.ReadToEnd();
                XDocument doc = XDocument.Parse(xml);
                var result1 = doc.Descendants().First().Descendants().First();
                lock (_lock)
                {
                    AsyncManager.Parameters["NewsTitle"] = result1.Elements().First().Value;
                }
                AsyncManager.OutstandingOperations.Decrement();
            }, null);
        }

        public ActionResult AsynchronousActionCompleted(string condition, string temp_f, string temp_c, string humidity, string latitude, string longitude, string NewsTitle)
        {
            ViewData["condition"] = condition;
            ViewData["temp_f"] = temp_f;
            ViewData["temp_c"] = temp_c;
            ViewData["humidity"] = humidity;
            ViewData["latitude"] = latitude;
            ViewData["longitude"] = longitude;
            ViewData["NewsTitle"] = NewsTitle;
            ViewData["Title"] = "Asynchronous Action";
            return View("SynchronousAction");
        }

         

           First of all, note that I am using Asynchronous Programming Model (APM) here. The benefit of using APM is that it uses Windows I/O Completion Port(IOCP) which is bound with I/O devices. When your I/O request methods(GetWeatherAsync etc) call webRequest.BeginGetResponse method then it will return immediately without waiting for I/O request to complete. When an I/O request completes, I/O device driver queue it at IOCP, which will then invoke your callback method using I/O thread pool thread. AsyncController is used here for supporting the APM in ASP.NET MVC application. Note also that I am using lock in I/O request methods because AsyncManager.Parameters is not thread safe.

           In this approach, your thread will not become idle for waiting I/O request(s) to complete. GetWeatherAsync, GetLatitudeAndLongitudeAsync and GetNewsAsync methods will return immediately after initiating the I/O request, without waiting for the I/O request to complete. Then this thread will become available to service other asp.net requests. When a I/O request is complete, your callback method will be executed using another(or same) thread pool thread. When all your callback methods has been executed, then AsynchronousActionCompleted method will be execute using the same thread pool thread that executed your last callback method. 

           This approach will overcome the two problems discussed in the first approach because now your request servicing thread will return immediately to the thread pool after initiating the I/O request and not wait for I/O request to complete. This will not make the individual request to run faster but will improve the overall scalability of your application. In some cases, this approach will run slower than the synchronous approach for individual request, but will improve the overall throughput of your application. So, for making the individual request to run quickly, you can return the response without web service data and load the web service data through multiple separate Ajax requests. You need to use SessionLess controller because multiple requests from single user will not run concurrently if the session is allowed to read and write. For details, see this. To solve this problem using SessionLess controller is given below,

 

        public class HomeController : AsyncController
        {

            ......................................
            ......................................
            ......................................

            public ActionResult SessionLessAction()
            {
                return View();
            }
        }


        [SessionState(SessionStateBehavior.Disabled)]
        public class SessionLessController : Controller
        {
            public ActionResult GetWeather()
            {
                WebRequest webRequest = WebRequest.Create("http://www.google.com/ig/api?weather=Karachi");
                var webResponse = webRequest.GetResponse();
                StreamReader stream = new StreamReader(webResponse.GetResponseStream());
                string xml = stream.ReadToEnd();
                XDocument doc = XDocument.Parse(xml);
                var current_conditions = doc.Descendants("weather").Descendants("current_conditions").First();
                var condition = current_conditions.Element("condition").Attribute("data").Value;
                var temp_f = current_conditions.Element("temp_f").Attribute("data").Value;
                var temp_c = current_conditions.Element("temp_c").Attribute("data").Value;
                var humidity = current_conditions.Element("humidity").Attribute("data").Value;
                return Json(new { condition = condition, tempF = temp_f, tempC = temp_c, humidity = humidity }, JsonRequestBehavior.AllowGet);
            }

            public ActionResult GetLatitudeAndLongitude()
            {
                WebRequest webRequest = WebRequest.Create("http://where.yahooapis.com/geocode?q=Karachi");
                var webResponse = webRequest.GetResponse();
                StreamReader stream = new StreamReader(webResponse.GetResponseStream());
                string xml = stream.ReadToEnd();
                XDocument doc = XDocument.Parse(xml);
                var result = doc.Descendants("ResultSet").Descendants("Result").First();
                var latitude = result.Element("latitude").Value;
                var longitude = result.Element("longitude").Value;
                return Json(new { latitude = latitude, longitude = longitude },JsonRequestBehavior.AllowGet);
            }

            public ActionResult GetNews()
            {
                WebRequest webRequest = WebRequest.Create("http://search.yahooapis.com/NewsSearchService/V1/newsSearch?query=karachi&results=1&appid=yahoodemo");
                var webResponse = webRequest.GetResponse();
                StreamReader stream = new StreamReader(webResponse.GetResponseStream());
                string xml = stream.ReadToEnd();
                XDocument doc = XDocument.Parse(xml);
                var result = doc.Descendants().First().Descendants().First();
                var NewsTitle = result.Elements().First().Value;
                return Json(new { news = NewsTitle }, JsonRequestBehavior.AllowGet);
            }

        }

 

          and here is the SessionLessAction view,

 

        @{
            ViewBag.Title = "SessionLess Action";
            Layout = "~/Views/Shared/_Layout.cshtml";
        }

        <h2>SessionLess Action</h2>
        <h3>Weather Information</h3>
        <b>Condition:</b> <span id="condition"><img src="@Url.Content("~/images/loading.gif")" alt="Loading"></span><br />
        <b>Temperature in Fahrenheit:</b> <span id="tempF"><img src="@Url.Content("~/images/loading.gif")" alt="Loading"></span><br />
        <b>Temperature in Celsius:</b> <span id="tempC"><img src="@Url.Content("~/images/loading.gif")" alt="Loading"></span><br />
        <b>Humidity:</b> <span id="humidity"><img src="@Url.Content("~/images/loading.gif")" alt="Loading"></span><br /><br />

        <h3>Location Information</h3>
        <b>Latitude:</b> <span id="latitude"><img src="@Url.Content("~/images/loading.gif")" alt="Loading"></span><br />
        <b>Longitude:</b> <span id="longitude"><img src="@Url.Content("~/images/loading.gif")" alt="Loading"></span><br /><br />

        <h3>News Information</h3>
        <b>News HeadLine:</b> <span id="news"><img src="@Url.Content("~/images/loading.gif")" alt="Loading"></span>
        <script type="text/javascript">
            $.getJSON('@Url.Action("GetWeather","SessionLess")', function (data) {
                $("#condition").html(data.condition);
                $("#tempF").html(data.tempF);
                $("#tempC").html(data.tempC);
                $("#humidity").html(data.humidity);
            });
            $.getJSON('@Url.Action("GetLatitudeAndLongitude","SessionLess")', function (data) {
                $("#latitude").html(data.latitude);
                $("#longitude").html(data.longitude);
            });
            $.getJSON('@Url.Action("GetNews","SessionLess")', function (data) {
                $("#news").html(data.news);
            });
        </script>

 

           In this view, I am using a loading image in each place of actual web service data and then loading the actual web service data through Ajax . Since I am using SessionLess controller, all Ajax requests to SessionLess controller will run concurrently. This way your page load quickly and load the time consuming web service data at background. This is the quickest approach to load the page than the first two described above. But you also need to be aware that every separate Ajax request will be executed by a thread pool thread. So, three separate Ajax requests in above example will use at least three thread pool threads.

           Deciding which approach to use and when depends on your application requirements. Use normal(synchronous) approach as much as possible because it only uses single thread pool thread to service the user'srequest. In fact, most of the time you only need the normal approach. However, if your application is getting data from a remote location which takes a long time to return or if your application needs to call a time consuming database query, etc, then you need to use asynchronous approach, which will efficiently utilize your thread idle time. However, still in this case, you need to stress test your application to decide whether to use synchronous or asynchronous approach because in asynchronous approach, context switching is performed and context switching may become bigger issue than thread idle time period issue. Finally, if you want your page to load quickly and load the time consuming data asynchronously, then you need to use SessionLess controller allows multiple requests to run concurrently. Twitter, Gmail, etc, uses this technique to load the page quickly and asynchronously load the page data. But keep in mind when you are using this technique that all separate Ajax request will execute inside a thread pool thread, which may affect the overall application performance. Also, keep in mind that some REST service provider provides data through JSONP, so you can directly request data from REST service provider through Ajax without scarifying a single thread pool thread. In a nutshell, you have to decide to use which approach when by reviewing your application requirements and making some stress test. But hopefully tips in this article will help you to decide which approach is suitable for your application.

 

   Summary:

          In this article, I showed you three ways to get data from remote web service; using normal controller action, using AsyncController controller action and using SessionLess controller action. I also explained each approach in detail and showed you when to use which approach. I am also attaching a sample application, which includes all three approaches discussed in this article. Hopefully you will enjoy this article too.


 

8 Comments

  • Interesting...Thx!

  • Does decorating the controller with the [SessionState(SessionStateBehavior.Disabled)] attribute achieve the same result as specifying in the web.config? Specifically when it comes to the performance benefit of running Ajax calls to actions on the controller concurrently?

  • >>Does decorating the controller with the [SessionState(SessionStateBehavior.Disabled)] attribute achieve the same result as specifying in the web.config?

    AFAIK, yes

    >>Specifically when it comes to the performance benefit of running Ajax calls to actions on the controller concurrently?

    I will use this technique when I have to show many distinct data in a page.

  • Excellent thanks.......

  • Very nice article! ;)

  • Great Article!

  • I might have missed something. But I did not understand the comparison. Your Async observation is correct and so is the example of Session controller. But are just different.
    Calling Asyc calls from the View was always possible thru jquery or otherwise.
    I do not see what value SessionlessController bring for those async calls? - besides these being calls that will not use sessions.

    Obviously I have missed something.

  • @akshay,
    This is possible to call async requests without using Session less controller. But they were not parallelized if you don't use Session less controller. See this for detail,
    http://weblogs.asp.net/imranbaloch/archive/2010/07/10/concurrent-requests-in-asp-net-mvc.aspx

    Clearly moving remote calls from your action to ajax makes your page to load quickly and asynchronously load remote data. There are lot of applications that uses this technique.

Comments have been disabled for this content.