Heart of MVC Paging

This entry is part 4 of 7 in the series Ultimate MVC Paging

This post was revised and updated on 10/24/2020.

If you fell on this post first, you’ll notice that the implementation of paging is a little more involved than the average example on the web. That’s because most of the other examples are either, limited, or outright “The wrong way to do it”. To start from the begining, click here: The Ultimate Guide to MVC Paging!.

Once you have a PagedResult object, or any object with the collection and total count of records that match the filters, back from your web service, you’ll need to convert it to a SearchModel that can be used throughout your MVC views and partial views to enable the paging.

In the implementation demo, we’re going to call a function to return the SearchModel, both from the original call to the Index controller, and then later from a Ajax when the user clicks on a page number or sort column. So I create a function that both controllers can share:

/// <summary>
/// Category search with paged results
/// </summary>
/// <param name="page"></param>
/// <param name="pageSize"></param>
/// <param name="searchTerm"></param>
/// <param name="sortFilter"></param>
/// <param name="sortColumn"></param>
/// <param name="sortDescending"></param>
/// <param name="ActiveOnly"></param>
/// <returns></returns>
public async Task<SearchModel<LuCategory>> GetCategoriesPagedAsync(int page = 1, 
            int pageSize = 25, 
            string searchTerm = "",
            string searchFilter = "", 
            string sortColumn = "ShortName",
            bool sortDescending = false, 
            bool ActiveOnly = true)
{
    SearchModel<LuCategory> _smlus = new SearchModel<LuCategory>();
    try
    {
        PagedResult<LuCategory> _cats = await LookupsSvc.GetCategoriesPagedAsync(page, pageSize, searchTerm, searchFilter, sortColumn, sortDescending, ActiveOnly);
        _smlus.TotalItems = _cats.TotalCount;
        _smlus.CurrentPage = page;
        _smlus.PageSize = pageSize;
        _smlus.RecordCount = _cats.Items.Count();
        _smlus.SortColumn = sortColumn;
        _smlus.SearchTerm = searchTerm;
        _smlus.SortDescending = sortDescending;
        _smlus.ActiveOnly = ActiveOnly;
        _smlus.SortedResults = _cats.Items.ToList();

        _smlus.actionName = "CatDisplay";
        _smlus.controllerName = "Lookups";

        return _smlus;

    }
    catch (Exception)
    {
        throw;
    }
}

While the code might be self explanatory, I’ll explain what’s happening here. Notice the two properties, actionName and contollerName. These are used by the partial views to know how to get to the controller method that’s going to serve up the partial view, so you can set it to go anywhere you like. The reason these properties are part of the object, is because this allows the paging partial views to be generic and reusable.

We would normally return the PagedResults object from our web service call. Then I transfer the results into the SearchModel while appending all the search, paging and filtering criteria passed into the method. The reason for this is that we want to retain this data in order to use the SearchModel in the Index view, as well as the partial views used for paging.

So lets go over the controllers first. The Index GET has all the parameters needed for the SearchModel with defaults, so that it can be called the first time with no arguments.

/// <summary>
/// Index of categories paged
/// </summary>
/// <param name="page"></param>
/// <param name="pageSize"></param>
/// <param name="searchTerm"></param>
/// <param name="sortFilter"></param>
/// <param name="sortColumn"></param>
/// <param name="sortDescending"></param>
/// <param name="ActiveOnly"></param>
/// <returns></returns>
[HttpGet]
public async Task<IActionResult> Index(int page = 1, int pageSize = 25, string searchTerm = "", string sortFilter = "", string sortColumn = "ShortName", bool sortDescending = false, bool ActiveOnly = true)
{
    try
    {
        var _smlus = await GetCategoriesPagedAsync(page, pageSize, searchTerm, sortFilter, sortColumn, sortDescending, ActiveOnly);

         return View(_smlus);

    }
    catch (Exception)
    {
        throw;
    }
}

Notice how having all the code in a sub method keeps our controller method clean and uncluttered.

Next, we need to have the controller method that returns the PartialView that will service the Ajax calls:

/// <summary>
/// Edit Category, with display of items
/// </summary>
/// <param name="id"></param>
/// <param name="pageSize"></param>
/// <returns></returns>
[HttpGet]
public async Task<ActionResult> Edit(long id, int pageSize = 25)
{
    LuCategoryModel model = new LuCategoryModel();
    try
    {
         if (id >= 0)
         {
             // Get the base LuCategory
             LuCategory _return = await LookupsSvc.GetLuCategoryByIdAsync(id);

            model = Helpers.MappingUtils.LuCatToLuCatModel(_return, false);

             // Get a paged result of some items because we want a paged list of child items as well
            model.Items = await GetItemsPagedAsync(id, 1, pageSize);
        }

        return View(model);

    }
    catch (Exception)
    {
        throw;
    }
}

This is all that’s needed to service the Index view, grid partial view, page count partial view and the pager partial view.


Series Navigation<< Business Objects for PagingMVC Paging Index View >>

Author: Jack Yasgar

Jack Yasgar has been developing software for various industries for two decades. Currently, he utilizes C#, JQuery, JavaScript, SQL Server with stored procedures and/or Entity Framework to produce MVC responsive web sites that converse to a service layer utilizing RESTful API in Web API 2.0 or Microsoft WCF web services. The infrastructure can be internal, shared or reside in Azure. Jack has designed dozens of relational databases that use the proper primary keys and foreign keys to allow for data integrity moving forward. While working in a Scrum/Agile environment, he is a firm believer that quality software comes from quality planning. Without getting caught up in analysis paralysis, it is still possible to achieve a level of design that allows an agile team to move forward quickly while keeping re-work to a minimum. Jack believes, “The key to long term software success is adhering to the SOLID design principles. Software written quickly, using wizards and other methods can impress the business sponsor / product owner for a short period of time. Once the honeymoon is over, the product owner will stay enamored when the team can implement changes quickly and fix bugs in minutes, not hours or days.” Jack has become certified by the Object Management Group as OCUP II (OMG Certified UML Professional) in addition to his certification as a Microsoft Certified Professional. The use of the Unified Modeling Language (UML) provides a visual guide to Use Cases and Activities that can guide the product owner in designing software that meets the end user needs. The software development teams then use the same drawings to create their Unit Tests to make sure that the software meets all those needs. The QA testing team can use the UML drawings as a guide to produce test cases. Once the software is in production, the UML drawings become a reference for business users and support staff to know what decisions are happening behind the scenes to guide their support efforts.

Leave a Reply

Your email address will not be published. Required fields are marked *