I have blogged a post on Database pagination in GridView using nHibernate before. But if someone not using nHibernate and using Database Store Procedure, this post will be useful. In this post I am using SQL Server 2008 (This method also will work SQL Server 2005 and higher versions).
I am using Northwind database for this example, so please make sure you installed the same database to test the code.
I have three projects for this example (created with layers as normal business applications have.. )
- DotNetTwitter.Entities - This project is used for defining the business entity class.
- DotNetTwitter.DataAccess - This project is used for database operation. Here is where the records are fetched for the required page.
- DotNetTwitter.DBPagination - Web Application which contains GridView to show the records
Database Script (To create ProductViewview).
Create View [dbo].[ProductView] As Select Products.ProductID, Products.ProductName, Suppliers.CompanyName, Categories.CategoryName, Products.QuantityPerUnit, Products.UnitPrice, Products.UnitsInStock, Products.UnitsOnOrder, Products.ReorderLevel from Products Join Suppliers on Suppliers.SupplierID = Products.SupplierID Join Categories on Categories.CategoryID = Products.CategoryID GO
Store Procedure
CREATE PROCEDURE [dbo].[SP_GET_PRODUCTS] @CurrentPageNo AS INT, @PageRecordsCount AS INT, @SortBy AS NVARCHAR(250), @SortType AS NVARCHAR(250), @TotalRecordCount AS INT OUTPUT AS SET NOCOUNT ON DECLARE @SQL AS NVARCHAR(1000) DECLARE @RecordsFrom AS INT DECLARE @RecordsTo AS INT -- RecordsFrom => For Ex: CurrentPage 2, PageRecordCount = 10, then (((2-1) * 10) + 1) => ((1 * 10) + 1) => 11 SET @RecordsFrom = (((@CurrentPageNo - 1) * @PageRecordsCount) + 1) -- @RecordsTo => For Ex: CurrentPage 2, PageRecordCount = 10, then (2 * 10) => 20 SET @RecordsTo = (@CurrentPageNo * @PageRecordsCount) IF @SortType = '' OR @SortType = NULL SET @SortType = 'Asc' -- Getting the Total Record Count SELECT @TotalRecordCount = COUNT(*) FROM ProductView -- Getting the records for a particular page SET @SQL = 'SELECT * FROM ' + '( ' + ' SELECT ' + ' ROW_NUMBER() OVER (ORDER BY ' + @SortBy + ' ' + @SortType + ') AS RowNumber, *' + ' FROM ProductView ' + ') AS RECORDS ' + 'Where RowNumber between ' + CAST(@RecordsFrom AS VARCHAR) + ' and ' + CAST(@RecordsTo AS VARCHAR) EXECUTE(@SQL)
Note: As I have created a View, I have used View in SP. Incase if you don’t follow creating View, you can use the following Store Procedure (infact both are same).
CREATE PROCEDURE [dbo].[SP_GET_PRODUCTS] @CurrentPageNo AS INT, @PageRecordsCount AS INT, @SortBy AS NVARCHAR(250), @SortType AS NVARCHAR(250), @TotalRecordCount AS INT OUTPUT AS SET NOCOUNT ON DECLARE @SQL AS NVARCHAR(1000) DECLARE @RecordsFrom AS INT DECLARE @RecordsTo AS INT -- RecordsFrom => For Ex: CurrentPage 2, PageRecordCount = 10, then (((2-1) * 10) + 1) => ((1 * 10) + 1) => 11 SET @RecordsFrom = (((@CurrentPageNo - 1) * @PageRecordsCount) + 1) -- @RecordsTo => For Ex: CurrentPage 2, PageRecordCount = 10, then (2 * 10) => 20 SET @RecordsTo = (@CurrentPageNo * @PageRecordsCount) IF @SortType = '' OR @SortType = NULL SET @SortType = 'Asc' -- Getting the Total Record Count -- SELECT @TotalRecordCount = COUNT(*) FROM ProductView -- Getting the records for a particular page SET @SQL = 'SELECT * FROM ' + '( ' + ' SELECT ' + ' ROW_NUMBER() OVER (ORDER BY ' + @SortBy + ' ' + @SortType + ') AS RowNumber, ' + ' Products.ProductID, ' + ' Products.ProductName, ' + ' Suppliers.CompanyName, ' + ' Categories.CategoryName, ' + ' Products.QuantityPerUnit, ' + ' Products.UnitPrice, ' + ' Products.UnitsInStock, ' + ' Products.UnitsOnOrder, ' + ' Products.ReorderLevel ' + ' FROM Products ' + ' INNER JOIN Suppliers ON Suppliers.SupplierID = Products.SupplierID ' + ' INNER JOIN Categories ON Categories.CategoryID = Products.CategoryID ' + ') AS RECORDS ' + 'Where RowNumber between ' + CAST(@RecordsFrom AS VARCHAR) + ' and ' + CAST(@RecordsTo AS VARCHAR) EXECUTE(@SQL) SET NOCOUNT OFF
I have three button and one text box in my gridview to navigate between pages (Next, Previous, Go and a Text box to enter PageNo). The Codebehind would be as
//C# Code behind
/// <summary>
/// Method which binds the data to the Grid
/// </summary>
private void BindGrid()
{
ProductDAO objProductDAO = new ProductDAO();
// Defining int variable to get the Total Record Count from Data layer
int totalRecordCount = 0;
// Get the SortByExpression and SortType from Hidden Control (by default, that will be updated in script)
string strSortExpression = ViewState["SortExpression"].ToString().Split(",".ToCharArray())[0];
SortType sortType = (SortType)Enum.Parse(typeof(SortType), ViewState["SortExpression"].ToString().Split(",".ToCharArray())[1], true);
// Getting how many records required to show in the Grid per page
int intPageRecordCount = Convert.ToInt32(ConfigurationManager.AppSettings["PageRecordCount"].ToString());
IList<ProductView> ProductViewList = objProductDAO.GetProducts(Convert.ToInt32(ViewState["CurrentPage"].ToString()), intPageRecordCount, strSortExpression, sortType, out totalRecordCount);
//Adding one empty row for just to show the grid
if (ProductViewList.Count == 0)
ProductViewList.Add(new ProductView());
grdViewProducts.DataSource = ProductViewList;
grdViewProducts.DataBind();
grdViewProducts.BottomPagerRow.Visible = true;
// Assign the Total Record count to
ViewState["TotalRecords"] = totalRecordCount.ToString();
Label lblPageInfo = grdViewProducts.BottomPagerRow.FindControl("lblPageInfo") as Label;
lblPageInfo.Text = "Page " + Convert.ToInt32(ViewState["CurrentPage"].ToString()).ToString() + " out of " + ((totalRecordCount % intPageRecordCount) > 0 ? (totalRecordCount / intPageRecordCount) + 1 : (totalRecordCount / intPageRecordCount)).ToString();
// Try to find the sorted column
for (int intRowIndex = 0; intRowIndex < grdViewProducts.Columns.Count; intRowIndex++)
{
if (strSortExpression == grdViewProducts.Columns[intRowIndex].SortExpression)
((LinkButton)grdViewProducts.HeaderRow.Cells[intRowIndex].Controls[0])
.CssClass = (sortType == SortType.Ascending ? "sortup" : "sortdown");
}
}
/// <summary>
/// Call when clicking Previous button on pagination
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btnPrevious_Click(object sender, EventArgs e)
{
if (Convert.ToInt32(ViewState["CurrentPage"].ToString()) > 1)
ViewState["CurrentPage"] = (Convert.ToInt32(ViewState["CurrentPage"].ToString()) - 1).ToString();
else
ViewState["CurrentPage"] = "1";
BindGrid();
}
/// <summary>
/// Call when clicking Go button on pagination
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btnGo_Click(object sender, EventArgs e)
{
int intPageRecordCount = Convert.ToInt32(ConfigurationManager.AppSettings["PageRecordCount"].ToString());
TextBox txtGoPage = ((TextBox)((ImageButton)sender).Parent.FindControl("txtGoPage"));
if (txtGoPage.Text.Trim().Length > 0)
{
if ((Convert.ToInt32(txtGoPage.Text) > 0) && (Convert.ToInt32(txtGoPage.Text) <= ((Convert.ToInt32(ViewState["TotalRecords"].ToString()) % intPageRecordCount) > 0 ? (Convert.ToInt32(ViewState["TotalRecords"].ToString()) / intPageRecordCount) + 1 : (Convert.ToInt32(ViewState["TotalRecords"].ToString()) / intPageRecordCount))))
{
ViewState["CurrentPage"] = Convert.ToInt32(txtGoPage.Text).ToString();
BindGrid();
}
}
}
/// <summary>
/// Call when clicking Next button on pagination
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btnNext_Click(object sender, EventArgs e)
{
int intPageRecordCount = Convert.ToInt32(ConfigurationManager.AppSettings["PageRecordCount"].ToString());
if (Convert.ToInt32(ViewState["CurrentPage"].ToString()) < ((Convert.ToInt32(ViewState["TotalRecords"].ToString()) % intPageRecordCount) > 0 ? (Convert.ToInt32(ViewState["TotalRecords"].ToString()) / intPageRecordCount) + 1 : (Convert.ToInt32(ViewState["TotalRecords"].ToString()) / intPageRecordCount)))
ViewState["CurrentPage"] = (Convert.ToInt32(ViewState["CurrentPage"].ToString()) + 1).ToString();
else
ViewState["CurrentPage"] = (Convert.ToInt32(ViewState["TotalRecords"].ToString()) / intPageRecordCount).ToString();
BindGrid();
}
/// <summary>
/// Sorts the GridView by clicked column.
/// </summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event argument.</param>
protected void grdViewProducts_Sorting(object sender, GridViewSortEventArgs e)
{
string strSortExpression = ViewState["SortExpression"].ToString().Split(",".ToCharArray())[0];
SortType sortType = (SortType)Enum.Parse(typeof(SortType), ViewState["SortExpression"].ToString().Split(",".ToCharArray())[1], true);
ViewState["CurrentPage"] = "1";
if (strSortExpression == e.SortExpression)
sortType = sortType == SortType.Ascending ? SortType.Descending : SortType.Ascending;
else
{
strSortExpression = e.SortExpression;
sortType = SortType.Ascending;
}
ViewState["SortExpression"] = e.SortExpression + "," + sortType.ToString();
BindGrid();
}'VB Code behind
''' <summary>
''' Method which binds the data to the Grid
''' </summary>
Private Sub BindGrid()
Dim objProductDAO As New ProductDAO()
' Defining int variable to get the Total Record Count from Data layer
Dim totalRecordCount As Integer = 0
' Get the SortByExpression and SortType from Hidden Control (by default, that will be updated in script)
Dim strSortExpression As String = ViewState("SortExpression").ToString().Split(",".ToCharArray())(0)
Dim sortType As SortType = DirectCast([Enum].Parse(GetType(SortType), ViewState("SortExpression").ToString().Split(",".ToCharArray())(1), True), SortType)
' Getting how many records required to show in the Grid per page
Dim intPageRecordCount As Integer = Convert.ToInt32(ConfigurationManager.AppSettings("PageRecordCount").ToString())
Dim ProductViewList As IList(Of ProductView) = objProductDAO.GetProducts(Convert.ToInt32(ViewState("CurrentPage").ToString()), intPageRecordCount, strSortExpression, sortType, totalRecordCount)
'Adding one empty row for just to show the grid
If ProductViewList.Count = 0 Then
ProductViewList.Add(New ProductView())
End If
grdViewProducts.DataSource = ProductViewList
grdViewProducts.DataBind()
grdViewProducts.BottomPagerRow.Visible = True
' Assign the Total Record count to
ViewState("TotalRecords") = totalRecordCount.ToString()
Dim lblPageInfo As Label = TryCast(grdViewProducts.BottomPagerRow.FindControl("lblPageInfo"), Label)
lblPageInfo.Text = "Page " & Convert.ToInt32(ViewState("CurrentPage").ToString()).ToString() & " out of " & (If((totalRecordCount Mod intPageRecordCount) > 0, (totalRecordCount \ intPageRecordCount) + 1, (totalRecordCount \ intPageRecordCount))).ToString()
' Try to find the sorted column
For intRowIndex As Integer = 0 To grdViewProducts.Columns.Count - 1
If strSortExpression = grdViewProducts.Columns(intRowIndex).SortExpression Then
DirectCast(grdViewProducts.HeaderRow.Cells(intRowIndex).Controls(0), LinkButton).CssClass = (If(sortType = sortType.Ascending, "sortup", "sortdown"))
End If
Next
End Sub
''' <summary>
''' Call when clicking Previous button on pagination
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
Protected Sub btnPrevious_Click(ByVal sender As Object, ByVal e As System.Web.UI.ImageClickEventArgs)
If Convert.ToInt32(ViewState("CurrentPage").ToString()) > 1 Then
ViewState("CurrentPage") = (Convert.ToInt32(ViewState("CurrentPage").ToString()) - 1).ToString()
Else
ViewState("CurrentPage") = "1"
End If
BindGrid()
End Sub
''' <summary>
''' Call when clicking Go button on pagination
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
Protected Sub btnGo_Click(ByVal sender As Object, ByVal e As System.Web.UI.ImageClickEventArgs)
Dim intPageRecordCount As Integer = Convert.ToInt32(ConfigurationManager.AppSettings("PageRecordCount").ToString())
Dim txtGoPage As TextBox = DirectCast(DirectCast(sender, ImageButton).Parent.FindControl("txtGoPage"), TextBox)
If txtGoPage.Text.Trim().Length > 0 Then
If (Convert.ToInt32(txtGoPage.Text) > 0) AndAlso (Convert.ToInt32(txtGoPage.Text) <= (If((Convert.ToInt32(ViewState("TotalRecords").ToString()) Mod intPageRecordCount) > 0, (Convert.ToInt32(ViewState("TotalRecords").ToString()) / intPageRecordCount) + 1, (Convert.ToInt32(ViewState("TotalRecords").ToString()) / intPageRecordCount)))) Then
ViewState("CurrentPage") = Convert.ToInt32(txtGoPage.Text).ToString()
BindGrid()
End If
End If
End Sub
''' <summary>
''' Call when clicking Next button on pagination
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
Protected Sub btnNext_Click(ByVal sender As Object, ByVal e As System.Web.UI.ImageClickEventArgs)
Dim intPageRecordCount As Integer = Convert.ToInt32(ConfigurationManager.AppSettings("PageRecordCount").ToString())
If Convert.ToInt32(ViewState("CurrentPage").ToString()) < (If((Convert.ToInt32(ViewState("TotalRecords").ToString()) Mod intPageRecordCount) > 0, (Convert.ToInt32(ViewState("TotalRecords").ToString()) / intPageRecordCount) + 1, (Convert.ToInt32(ViewState("TotalRecords").ToString()) / intPageRecordCount))) Then
ViewState("CurrentPage") = (Convert.ToInt32(ViewState("CurrentPage").ToString()) + 1).ToString()
Else
ViewState("CurrentPage") = (Convert.ToInt32(ViewState("TotalRecords").ToString()) / intPageRecordCount).ToString()
End If
BindGrid()
End Sub
''' <summary>
''' Sorts the GridView by clicked column.
''' </summary>
''' <param name="sender">The event sender.</param>
''' <param name="e">The event argument.</param>
Protected Sub grdViewProducts_Sorting(ByVal sender As Object, ByVal e As GridViewSortEventArgs)
Dim strSortExpression As String = ViewState("SortExpression").ToString().Split(",".ToCharArray())(0)
Dim sortType As SortType = DirectCast([Enum].Parse(GetType(SortType), ViewState("SortExpression").ToString().Split(",".ToCharArray())(1), True), SortType)
ViewState("CurrentPage") = "1"
If strSortExpression = e.SortExpression Then
sortType = If(sortType = sortType.Ascending, sortType.Descending, sortType.Ascending)
Else
strSortExpression = e.SortExpression
sortType = sortType.Ascending
End If
ViewState("SortExpression") = Convert.ToString(e.SortExpression) & "," & sortType.ToString()
BindGrid()
End SubBelow is the code from data access class, which returns only records for particular page.
//C# Code
public IList<ProductView> GetProducts(int currentPageNo, int pageRecordsCount, string sortBy, SortType sortType, out int totalRecordCount)
{
SqlConnection connection = null;
SqlDataReader dataReader = null;
try
{
IList<ProductView> productViewList = new List<ProductView>();
connection = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ToString());
SqlDataAdapter dataAdaptor = new SqlDataAdapter("SP_GET_PRODUCTS", connection);
dataAdaptor.SelectCommand.CommandType = System.Data.CommandType.StoredProcedure;
dataAdaptor.SelectCommand.Parameters.Add(new SqlParameter("@CurrentPageNo", SqlDbType.Int));
dataAdaptor.SelectCommand.Parameters["@CurrentPageNo"].Value = currentPageNo;
dataAdaptor.SelectCommand.Parameters.Add(new SqlParameter("@PageRecordsCount", SqlDbType.Int));
dataAdaptor.SelectCommand.Parameters["@PageRecordsCount"].Value = pageRecordsCount;
dataAdaptor.SelectCommand.Parameters.Add(new SqlParameter("@SortBy", SqlDbType.VarChar));
dataAdaptor.SelectCommand.Parameters["@SortBy"].Value = sortBy;
dataAdaptor.SelectCommand.Parameters.Add(new SqlParameter("@SortType", SqlDbType.VarChar));
dataAdaptor.SelectCommand.Parameters["@SortType"].Value = (sortType == SortType.Ascending ? "Asc" : "Desc");
dataAdaptor.SelectCommand.Parameters.Add(new SqlParameter("@TotalRecordCount", SqlDbType.Int));
dataAdaptor.SelectCommand.Parameters["@TotalRecordCount"].Direction = ParameterDirection.Output;
connection.Open();
DataSet dataSet = new DataSet();
dataAdaptor.Fill(dataSet);
foreach(DataRow dataRow in dataSet.Tables[0].Rows)
{
ProductView productView = new ProductView();
productView.ProductID = Convert.ToInt32(dataRow["ProductID"].ToString());
productView.ProductName = dataRow["ProductName"].ToString();
productView.CompanyName = dataRow["CompanyName"].ToString();
productView.CategoryName = dataRow["CategoryName"].ToString();
productView.QuantityPerUnit = dataRow["QuantityPerUnit"].ToString();
productView.UnitPrice = Convert.ToDouble(dataRow["UnitPrice"].ToString());
productView.UnitsInStock = Convert.ToInt32(dataRow["UnitsInStock"].ToString());
productView.UnitsOnOrder = Convert.ToInt32(dataRow["UnitsOnOrder"].ToString());
productView.ReorderLevel = Convert.ToInt32(dataRow["ReorderLevel"].ToString());
productViewList.Add(productView);
}
totalRecordCount = Convert.ToInt32(dataAdaptor.SelectCommand.Parameters["@TotalRecordCount"].Value);
connection.Close();
//return
return productViewList;
}
catch (Exception ex)
{
if (connection != null) connection.Close();
if (dataReader != null) dataReader.Close();
// Log
throw ex;
}
}
'VB Code
Public Function GetProducts(ByVal currentPageNo As Integer, ByVal pageRecordsCount As Integer, ByVal sortBy As String, ByVal sortType__1 As SortType, ByRef totalRecordCount As Integer) As IList(Of ProductView)
Dim connection As SqlConnection = Nothing
Dim dataReader As SqlDataReader = Nothing
Try
Dim productViewList As IList(Of ProductView) = New List(Of ProductView)()
connection = New SqlConnection(ConfigurationManager.ConnectionStrings("ConnectionString").ToString())
Dim dataAdaptor As New SqlDataAdapter("SP_GET_PRODUCTS", connection)
dataAdaptor.SelectCommand.CommandType = System.Data.CommandType.StoredProcedure
dataAdaptor.SelectCommand.Parameters.Add(New SqlParameter("@CurrentPageNo", SqlDbType.Int))
dataAdaptor.SelectCommand.Parameters("@CurrentPageNo").Value = currentPageNo
dataAdaptor.SelectCommand.Parameters.Add(New SqlParameter("@PageRecordsCount", SqlDbType.Int))
dataAdaptor.SelectCommand.Parameters("@PageRecordsCount").Value = pageRecordsCount
dataAdaptor.SelectCommand.Parameters.Add(New SqlParameter("@SortBy", SqlDbType.VarChar))
dataAdaptor.SelectCommand.Parameters("@SortBy").Value = sortBy
dataAdaptor.SelectCommand.Parameters.Add(New SqlParameter("@SortType", SqlDbType.VarChar))
dataAdaptor.SelectCommand.Parameters("@SortType").Value = (If(sortType__1 = SortType.Ascending, "Asc", "Desc"))
dataAdaptor.SelectCommand.Parameters.Add(New SqlParameter("@TotalRecordCount", SqlDbType.Int))
dataAdaptor.SelectCommand.Parameters("@TotalRecordCount").Direction = ParameterDirection.Output
connection.Open()
Dim dataSet As New DataSet()
dataAdaptor.Fill(dataSet)
For Each dataRow As DataRow In dataSet.Tables(0).Rows
Dim productView As New ProductView()
productView.ProductID = Convert.ToInt32(dataRow("ProductID").ToString())
productView.ProductName = dataRow("ProductName").ToString()
productView.CompanyName = dataRow("CompanyName").ToString()
productView.CategoryName = dataRow("CategoryName").ToString()
productView.QuantityPerUnit = dataRow("QuantityPerUnit").ToString()
productView.UnitPrice = Convert.ToDouble(dataRow("UnitPrice").ToString())
productView.UnitsInStock = Convert.ToInt32(dataRow("UnitsInStock").ToString())
productView.UnitsOnOrder = Convert.ToInt32(dataRow("UnitsOnOrder").ToString())
productView.ReorderLevel = Convert.ToInt32(dataRow("ReorderLevel").ToString())
productViewList.Add(productView)
Next
totalRecordCount = Convert.ToInt32(dataAdaptor.SelectCommand.Parameters("@TotalRecordCount").Value)
connection.Close()
'return
Return productViewList
Catch ex As Exception
If connection IsNot Nothing Then
connection.Close()
End If
If dataReader IsNot Nothing Then
dataReader.Close()
End If
' Log
Throw ex
End Try
End FunctionThis code has been tested with IE 6.0/8.0, Chrome 10.0, Firefox 3.6, Opera 11.01
Here is the output of the example.
![]() |
| Initial Screen (by default Product Name sorted with first page) |
![]() |
| Sorted by Category |
![]() |
| Sorted in Desc order and showing 3rd page |
![]() |
| Skip to 4th page (usage of Go page) |
You can see the output in video here
download the working example of the source code in C# here and in VB here



can you please Demonstrate paging with strongly typed custom classes or Generics rather than datasets or tableAdapters as datasets are not suitable if we have huge amount of records..?
Hi Shashavali,
I am not sure I understood correctly. But, looking at the code I am using custom class only for getting the records to the UI layer and binding to the GridView.
public IList GetProducts(){}
This method returns collection of ProductView class.
If I am missing something here, pls reply me the requirement more or send me to my mail id.