Sean Chase Blog The frenetic soapbox

Mar 30


MVC 3 - Search Form & Result Paging

  • Created: Wednesday, March 30, 2011
  • Sean

Developing a search form should be one of the simplest Web development tasks in the world. I can do it in a few minutes with ASP.NET WebForms, but for some reason with MVC it kicked my ass! Then I remembered back to the good old 90s when contract rates were high, the economy was great, and I was 20lbs lighter.

...oh and classic ASP forced you to create forms where you could either do a POST or a GET. Remember GET? Yeah it's beeen a while if you've been an ASP.NET developer. But welcome back! That is...if you are using MVC.

It is actually pretty simple. If, unlike me, you by chance bump into the blog post instead of trying to find the (IMO) really BAD answers on (at least to-date) and various corners of the Interwebz googling things with Bing or binging things with Google....but I digress.

Here's the answer:

  1. Make your form do an HTTP GET.
  2. Use Nuget to download PagedList
  3. Use Model Binding in your controller action method.
  4. Use RouteValueDictionary in the PagedList Pager HTML helper.
  5. ???
  6. Profit!

Here's the code for the ViewModel class, controller, and View (in that order):

using PagedList;


namespace SearchFormResultPagingExample.Models {

    public class SearchViewModel {

        public int? Page { get; set; }

        public string EmailAddress { get; set; }

        public string LastName { get; set; }

        public IPagedList<Contact> SearchResults { get; set; }

        public string SearchButton { get; set; }




using System.Linq;

using System.Web.Mvc;

using SearchFormResultPagingExample.Models;

using PagedList; //NOTE: use Nuget to reference PagedList


namespace SearchFormResultPagingExample.Controllers {

    public class SearchController : Controller {

        const int RecordsPerPage = 25;


        public ActionResult Index(SearchViewModel model) {

            if (!string.IsNullOrEmpty(model.SearchButton) || model.Page.HasValue) {

                var entities = new AdventureWorksEntities();

                var results = entities.Contacts.Where(c => c.LastName.StartsWith(model.LastName) && c.EmailAddress.StartsWith(model.EmailAddress))

                    .OrderBy(o => o.LastName);


                var pageIndex = model.Page ?? 0;

                model.SearchResults = results.ToPagedList(pageIndex, 25);


            return View(model);





@model SearchFormResultPagingExample.Models.SearchViewModel

@using PagedList.Mvc;


@using (Html.BeginForm("Index", "Search", FormMethod.Get)) {



        <legend>Contact Searchlegend>


        <div class="editor-label">

            @Html.LabelFor(model => model.EmailAddress)


        <div class="editor-field">

            @Html.EditorFor(model => model.EmailAddress)

            @Html.ValidationMessageFor(model => model.EmailAddress)



        <div class="editor-label">

            @Html.LabelFor(model => model.LastName)


        <div class="editor-field">

            @Html.EditorFor(model => model.LastName)

            @Html.ValidationMessageFor(model => model.LastName)



            <input name="SearchButton" type="submit" value="Search" />





@if (Model.SearchResults != null && Model.SearchResults.Count > 0) {

    foreach (var result in Model.SearchResults) {

            <hr />

               <table width="100%">


                             <td valign="top" width="*">

                        <div style="font-weight: bold; font-size:large;">@result.LastName, @result.FirstNamediv>

                        @result.Title<br />

                        @result.Phone<br />






        <hr />



            page => Url.Action("Index", new RouteValueDictionary() {

               { "Page", page },

               { "EmailAddress", Model.EmailAddress },

               { "LastName", Model.LastName }




The magic is that MVC coerces the querystring back into the model and vice versa.

Hope this helps anyone trying to figure this out.