Custom paging with Subsonic 3 and ObjectDataSource in ASP.NET

Here is how I created a paged items list in ASP.NET 3.5 using Subsonic for data access, and ASP.NET Controls for the display.

I use Subsonic 3.0.0.4 with ActiveRecord and ASP.NET 3.5 SP1 webforms.

For this example, we are creating a blog post list and that the DAL is created (the data class we use is “DAL.BlogPost”).

The DAL.BlogPost object has three properties we need : CreatedOn, Subject and Body.

First step : prepare your data manager class


namespace MyBlog.BLL
{
   public class PostManager
   {
     // ...
   }
}

(keep in mind the namespace and the class for the nexts steps)

In this class, two methods are required,

The first returns the total number of items


public int GetPostCount()
{
  return DAL.BlogPost.All().Count(x => x.IsDeleted != true);
}

The second one returns the items contained in a defined range (start index and then nomber of elements), so this method needs to know where to start and how many items to get.


public PagedList<DAL.BlogPost> GetLastestPosts(int startRowIndex, int maximumRows)
{
   if (startRowIndex != 0)
   {
     startRowIndex =(startRowIndex/maximumRows);
   }

    IOrderedQueryable<DAL.BlogPost> yourQuery = DAL.BlogPost.All().OrderByDescending(x => x.CreatedOn);
    return new PagedList<DAL.BlogPost>(yourQuery, startRowIndex, maximumRows);
}

One important part to remember when using Subsonic is


if (startRowIndex != 0)
{
   startRowIndex = startRowIndex / maximumRows;
}

This conversion is necessary because subsonic and the ObjectDataSource doesn’t share the same opinion about the paging method.
Subsonic says

“I want the third page containing five elements”

ObjectDataSource says

“I want the five elements following the third element”

So the conversion is mandatory if you want a correct paging.

Second step : create the aspx page.

Declare an ObjectDataSource:

<asp:ObjectDataSource runat="server" ID="postsDataSource"
 TypeName="Example.BLL.PostManager"
 SelectMethod="GetLastestPosts"
 EnablePaging="true"
 SelectCountMethod="GetPostCount" >
 </asp:ObjectDataSource>

Note the TypeName attribute : the value is the namespace + the class name of our datamanager class.
We set the EnablePaging attribute to true and we specify the SelectCountMethod and the SelectMethod.

We don’t need any parameters for the paging, by default the MaximumRowsParameterName and StartRowIndexParameterName are set to “maximumRows” and “startRowIndex”.
If we want other parameters names in our datamanager class, we can define them in the ObjectDataSource with these two parameters.

Add an asp ListView element

<asp:ListView runat="server" ID="postsListview"
     DataSourceID="postsDataSource">
 <LayoutTemplate>
    <asp:PlaceHolder ID="itemPlaceHolder"
         runat="server" />
 </LayoutTemplate>
 <ItemTemplate>
    <h2><%# Eval("CreatedOn","{0:d}")%> :
     <%# Eval("Subject") %></h2>
    <br />
    <p>
     <%# Eval("Body") %>
    </p>
    <hr />
 </ItemTemplate>
</asp:ListView>

Set the DataSourceID attribute to the ObjectDataSource ID parameter.

Define a LayoutTemplate and an ItemTemplate with the elements we want to display.

Then add an asp datapager control in the LayoutTemplate


<LayoutTemplate>

  <asp:DataPager ID="postsDataPager" runat="server"
       PageSize="3">
    <Fields>
      <asp:NextPreviousPagerField ButtonType="Link"
           FirstPageText="<<" PreviousPageText="<"
           ShowNextPageButton="false"
           ShowFirstPageButton="true" />
      <asp:NumericPagerField PreviousPageText="..."
           NextPageText="..." ButtonCount="10" />
      <asp:NextPreviousPagerField ButtonType="Link"
           LastPageText=">>" NextPageText=">"
           ShowPreviousPageButton="false"
           ShowLastPageButton="true" />
     </Fields>
   </asp:DataPager>

   <asp:PlaceHolder ID="itemPlaceHolder" runat="server" />
 </LayoutTemplate>

Here is the result :
blogposts

Remark about the data access method

It’s possible to avoid the start index value conversion !

Check the PagedList first constructor code (code is available on the subsonic git repository)


public PagedList(IQueryable<T> source, int index, int pageSize)
{
   int total = source.Count();
   TotalCount = total;
   TotalPages = total / pageSize;

   if(total % pageSize > 0)
     TotalPages++;

   PageSize = pageSize;
   PageIndex = index;
   AddRange(source.Skip(index * pageSize).Take(pageSize).ToList());
}

As you can see, Subsonic use the Skip and the Take methods from Linq

So, you can modify the GetLastestPosts method to return a strongly typed List in place of the PagedList:


public List<DAL.BlogPost> GetLastestPosts(int startRowIndex, int maximumRows)
{
   return DAL.BlogPost.All().
          Skip(startRowIndex).
          Take(maximumRows).
          OrderByDescending(x => x.CreatedOn).
          ToList<DAL.BlogPost>();
}

With this method, no more conversion needed

Incoming search terms:

  • how to use subsonic in visual studio 2015

About Sam Beauvois

Application Developer, .NET enthusiast since 2004, I'm interested in technology watch, usability, code quality, patterns & practices, UX, ...

2 comments

  1. Lloyd says:

    This article was extremely helpful – by far the best I’ve seen on the subject and I’ve been googling it a plenty – but while this takes care of the paging it doesn’t address the need for sorting. Assuming you still use subsonic, what are your recommendations for handling sort through the ODS?

    Thanks in advance,

  2. [...] This post was mentioned on Twitter by Didier Danse, Samuel Beauvois. Samuel Beauvois said: New blog post : Custom paging with Subsonic 3 and ObjectDataSource in ASP.NET. http://bit.ly/djQvKH [...]

Leave a Reply

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>