Lazy Loading In ASP.NET MVC

Introduction
 
Lazy loading is a technique which loads the data on demand or when it is required. It improves efficiency and the performance of the application. Let's take a scenario, where we have 1 lakh of records and want to display them to the user. However, when you load 1 lakh of records at a time, it takes more time to render the result. It may be off putting to the user, while loading the records. In order to avoid the problem, either we need to use paging concept or lazy loading. Lazy loading loads the data step-by-step, when the user scrolls down the page, which is required by it.
 
Requirement- We have 500 records in our DataSource (text file or the database). It displays 20 records, when the page loads the first time. It displays the next records, when the user scroll down the page.
 
Using Code
 
To describe technically the requirement stated above, we will use DataSource as a text file, which has 500 records. Subsequently, controller's action loads 20 records for the first time. When the user scrolls down, it sends an AJAX request to the Server to load the next records. JavaScript code snippet checks when the user scrolls down by checking Window height and scroll height. The snapshot is gif project structure and follows the steps given below.
 
 
Figure 1: Project structure of Lazy Loading  
 
Model 
 
Let's design Project entity. It contains properties, which are ID, Name, ManagerName and Email.
  1. public class Project  
  2. {  
  3.     public string ID { getset; }  
  4.     public string Name { getset; }  
  5.     public string ManagerName { getset; }  
  6.     public string Email { getset; }  

Now, it loads the data from the text file, which contains all the project related information. After getting the text file contents, it loops over line by line and creates new project object with the required properties.
  1. public List<Project> GetProjectList()  
  2. {  
  3.     string projectFile = HostingEnvironment.MapPath("~/App_Data/Projects.txt");  
  4.     List<Project> tempList = new List<Project>();  
  5.   
  6.     foreach (string line in File.ReadAllLines(projectFile))  
  7.     {  
  8.         var parts = line.Split('|');  
  9.         tempList.Add(new Project()  
  10.         {  
  11.             ID = parts[0],  
  12.             Name = parts[1],  
  13.             ManagerName= parts[2],  
  14.             Email = parts[3]  
  15.         });  
  16.     }  
  17.   
  18.     return tempList;  

Controller
 
It contains the actions given below.
  • Index()
    It is the default action, when we browse home controller and it redirects to GetProject() action.

  • GetProjects()
    This function checks whether it is simple AJAX request. If it is yes, it returns the data with Partial View, else it returns the data through ViewBag.

  • GetRecordsForPage()
    It receives the page number as the parameter. It uses LINQ to get the required no of records data from DataSource.
  1. public const int RecordsPerPage = 20;  
  2. public List<Project> ProjectData;  
  3.   
  4. public HomeController()  
  5. {  
  6.     ViewBag.RecordsPerPage = RecordsPerPage;              
  7. }  
  8.   
  9. public ActionResult Index()  
  10. {  
  11.     return RedirectToAction("GetProjects");  
  12. }          
  13.   
  14. public ActionResult GetProjects(int? pageNum)  
  15. {  
  16.     pageNum = pageNum ?? 0;  
  17.     ViewBag.IsEndOfRecords = false;  
  18.     if (Request.IsAjaxRequest())  
  19.     {  
  20.         var projects = GetRecordsForPage(pageNum.Value);  
  21.         ViewBag.IsEndOfRecords = (projects.Any());  
  22.         return PartialView("_ProjectData", projects);  
  23.     }  
  24.     else  
  25.     {  
  26.         var projectRep = new ProjectRepository();  
  27.         ProjectData = projectRep.GetProjectList();  
  28.   
  29.         ViewBag.TotalNumberProjects = ProjectData.Count;  
  30.         ViewBag.Projects = GetRecordsForPage(pageNum.Value);  
  31.   
  32.         return View("Index");  
  33.     }  
  34. }  
  35.   
  36. public List<Project> GetRecordsForPage(int pageNum)  
  37. {  
  38.     var projectRep = new ProjectRepository();  
  39.     ProjectData = projectRep.GetProjectList();  
  40.   
  41.     int from = (pageNum * RecordsPerPage);  
  42.   
  43.     var tempList = (from rec in ProjectData  
  44.                     select rec).Skip(from).Take(20).ToList<Project>();  
  45.   
  46.     return tempList;  

Views
 
To define Views module, we will discuss about _Layout.cshtml, Index.cshtml and _Projectdata.cshtml. 
 
_Layout.cshtml
 
It acts as a master page, which maintains consistent layout. In header section, it injects all common js and css files. In body section, it has RenderBody(), where child page content will render. This page reference is added in _ViewStart.cshtml page (Layout = "~/Views/Shared/_Layout.cshtml").
  1. <!DOCTYPE html>  
  2. <html>  
  3. <head>  
  4.     <meta charset="utf-8" />  
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">  
  6.     <title>@ViewBag.Title</title>  
  7.     <script src="~/Scripts/jquery-1.10.2.min.js"></script>  
  8.     <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>  
  9.     <script src="~/Scripts/bootstrap.min.js"></script>  
  10.     <link href="~/Content/bootstrap.min.css" rel="stylesheet" />  
  11.     <link href="~/Content/Site.css" rel="stylesheet" />  
  12. </head>  
  13. <body>  
  14.     <div class="container body-content">  
  15.         @RenderBody()  
  16.   
  17.         <hr />  
  18.   
  19.         <div id="loading">  
  20.             <img src='~/Content/spin.gif' /><p style="color: red;"><b>Loading Next...</b></p>  
  21.         </div>  
  22.   
  23.         <footer>  
  24.             <p>Copyright© @DateTime.Now.Year </p>  
  25.         </footer>  
  26.     </div>  
  27.     @RenderSection("scripts", required: false)  
  28. </body>  
  29. </html> 
Index.cshtml
 
This page acts as a child page and it stays in Views/Home/Index.cshtml directory.  It defines HTML code to display the records and also injects the required JavaScript files. 
  1. @using LazyLoadInMVC.Models  
  2. @{  
  3.     ViewBag.Title = "LazyLoading Demo";  
  4. }  
  5. <div class="jumbotron">  
  6.     <h1>LazyLoading Demo</h1>  
  7.     <p class="lead">  
  8.         This page is demo how to load records on demand.  
  9.         Instead of loading all <span class="text-primary">@ViewBag.TotalNumberProjects</span> records,   
  10.         let's load based on user requirement.  
  11.   
  12.         Scroll down the page then you will see that records are added to page.  
  13.         It loads @ViewBag.RecordsPerPage records at a time and total no of records is <span class="text-primary">@ViewBag.TotalNumberProjects</span>.  
  14.     </p>  
  15. </div>  
  16.   
  17. <table class="table table-striped table-bordered table-condensed infinite-scroll">  
  18.         <thead>  
  19.             <tr>  
  20.                 <th>ID #</th>  
  21.                 <th>Name</th>  
  22.                 <th>Manager>  
  23.                 <th>Manager Email</th>  
  24.             </tr>  
  25.         </thead>  
  26.         <tbody>  
  27.             @Html.Partial("_ProjectData", (ViewBag.Projects as List<Project>))  
  28.         </tbody>  
  29.     </table>  
  30.   
  31. @section scripts{  
  32.     <script src="~/Scripts/lazyLoading.js"></script>  
  33.     <script type="text/javascript">  
  34.         $(function () {  
  35.             $("div#loading").hide();  
  36.         });  
  37.   
  38.         var url = '@Url.RouteUrl("ProjectDataList")';  
  39.         $(window).scroll(scrollHandler);  
  40.     </script>  

In the preceding code, it defines Url.RouteUrl(), which is defined in RouteConfig.cs file. Here, the code is given below. 
  1. public static void RegisterRoutes(RouteCollection routes)  
  2. {  
  3.     routes.IgnoreRoute("{resource}.axd/{*pathInfo}");  
  4.   
  5.     routes.MapRoute("ProjectDataList"""new { controller = "Home", action = "GetProjects" });  
  6.   
  7.     routes.MapRoute(  
  8.         name: "Default",  
  9.         url: "{controller}/{action}/{id}",  
  10.         defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }  
  11.     );  
  12. }  
_ProjectData.cshtml
 
This page is a Partial View, which defines the loop over and creates rows for the same. It will be used, when we send an AJAX request to the server and get the data with HTML format. 
  1. @model List<LazyLoadInMVC.Models.Project>  
  2.   
  3. @foreach (var cust in Model)  
  4. {  
  5.     <tr>  
  6.         <td>@cust.ID</td>  
  7.         <td>@cust.Name</td>  
  8.         <td>@cust.ManagerName</td>  
  9.         <td>@cust.Email</td>  
  10.     </tr>  

JavaScript 
 
It's time to define JavaScript, which performs Lazy Loading. In the code snippet given below, scrollHandler() is a function, which calls when any scroll event occurs in the Browser. The function checks if the difference between document height and the window height is greater than document scrollTop(). If it is true, it sends an AJAX request by increasing the page number as a parameter. On success of AJAX, it appends the data to the table.
  1. var page = 0,  
  2.     inCallback = false,  
  3.     isReachedScrollEnd = false;  
  4.   
  5. var scrollHandler = function () {  
  6.     if (isReachedScrollEnd == false &&  
  7.         ($(document).scrollTop() <= $(document).height() - $(window).height()))  
  8.     {  
  9.         loadProjectData(url);  
  10.     }  
  11. }  
  12.   
  13. function loadProjectData(loadMoreRowsUrl) {  
  14.     if (page > -1 && !inCallback) {  
  15.         inCallback = true;  
  16.         page++;  
  17.         $("div#loading").show();  
  18.   
  19.         $.ajax({  
  20.             type: 'GET',  
  21.             url: loadMoreRowsUrl,  
  22.             data: "pageNum=" + page,  
  23.             success: function (data, textstatus) {  
  24.                 if (data != '') {  
  25.                     $("table.infinite-scroll > tbody").append(data);  
  26.                     $("table.infinite-scroll > tbody > tr:even").addClass("alt-row-class");  
  27.                     $("table.infinite-scroll > tbody > tr:odd").removeClass("alt-row-class");  
  28.                 }  
  29.                 else {  
  30.                     page = -1;  
  31.                 }  
  32.   
  33.                 inCallback = false;  
  34.                 $("div#loading").hide();   
  35.             },  
  36.             error: function (XMLHttpRequest, textStatus, errorThrown) {  
  37.                 alert(errorThrown);  
  38.             }  
  39.         });  
  40.     }  

Output
 
Once we are done with changes, let's browse the application. By default, it loads 20 records and when you scroll down, it loads the next 20 records with loading icon. See in Figure 1 the last project ID is 1019, Figure 2 it is 1039. Thus, you can know the data loads when scrolling down the page takes place.
 
Figure 2: Load records for first time
 
 
Figure 3: Load records for second time
 
Conclusion
 
Here, we discussed about lazy loading and how it is implemented and helpful. Lazy loading is a pattern to load the data on demand. It is useful when you have large amount of records and you need to display those. It loads the data step by step when the user scrolls down or needs it. By following this, you can deliver a better experience to your users, better performance,  and also a more efficiently managed screen.


Similar Articles