Part 1 - The Database
I will be writing a multi-part article on how I implemented databse paging and sorting with CSLA business objects for an ASP.NET application. As a disclaimer, I am sure there are other and potentially better solutions, but I wanted to offer up my solution in hope that it will help others.
The Problem - When dealing with large amounts of data, it is inefficient to retrieve thounsands of records from a database only to display 10 - 20 records on your UI. The default paging within a DataGrid does just this. In addition, each time a user "sorts" a column on a datagrid, all of the records are again being retrieved from the databse. An alternative to the approach I am using is to use caching within your application to address these issues.
The Solution - I will create a small ASP.NET application, similar to the Project Tracker solution provided with the CSLA.NET framework. The application I am building is meant to hold "Information", anything from Birthdates to interesting websites, and allows the user to search, sort, add, update, and remove "Information" items. The homepage will contain a GridView that displays all of the information items contained within the database. My solution uses a SQL Server 2005 database. In order to create a layered, Object Oreinted application that is performant, I am using CSLA.Net business objects with Database paging and sorting.
The Database - I am using SQL Server 2005 as my database. SQL 2005 provides a new function ROW_NUMBER() that along with CTE's provides an easy and performant way to select a subset of data for paging. The below stored procedure will be called from my application which will pass all of the necessary parameters. Note that this procedure uses dynamic sql. You should only use dynamic sql if you fully understand SQL Injection attacks and how to protect your code from them. In addition, SQL Server will not cache the query execution plan for dynamic sql, so there is a performance trade off. In this case, being able to limit the nunber of records returned to the middle tier will be worthwhile once your table becomes large with many records. Also, using EXEC sp_executesql will allow the query plan to be cached, but in my case, since I am also sorting, I would not be able to paramaterize the order by using sp_executesql.
What is next...
In my next article I will explain what needs to be done within your CSLA business objects to call this stored procedure. My final article will expain how to hook this functionality up to a GridView in an ASP.Net page.
CREATE PROCEDURE [dbo].[GetInfoList]
(
@PageIndex int,
@NumberOfRecords int,
@Sort varchar(200)
)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @Sql varchar(5000);
SELECT @Sql = ' SELECT InfoList.InfoId, InfoList.Subject, InfoList.CategoryName,
InfoList.Description, InfoList.LastModified FROM
(
SELECT i.InfoId, i.Subject, c.CategoryName,
SUBSTRING(i.Description,0,30) Description, i.LastModified,
ROW_NUMBER() OVER (ORDER BY ' + @Sort + ') AS RowNum
FROM dbo.Info i
JOIN dbo.Category c on c.CategoryId = i.CategoryId ) InfoList '
SELECT @Sql = @Sql + ' WHERE InfoList.RowNum BETWEEN
( ' + CAST(@PageIndex AS varchar(10)) + ' * ' + CAST(@NumberOfRecords AS varchar(10)) + ' + 1 ) AND (( '
+ CAST(@PageIndex AS varchar(10)) + ' + 1 ) * ' + CAST(@NumberOfRecords AS varchar(10)) + ' ) '
SELECT @Sql = @Sql + ' ORDER BY ' + @Sort;
EXECUTE(@Sql);
--Return total number of records for paging
SELECT COUNT(*) TotalRowCount FROM dbo.Info;
END