TheChaseMan's Frenetic SoapBox

Always looking for better ways to do things...

Update: GridView Custom Paging with ObjectDataSource

About a month ago I posted some code for implementing custom paging with the GridView and ObjectDataSource with the goal being a single database call to get the records and the rowcount. The code I posted involved some hackery in that I was passing around a ObjectDataSourceSelectingEventArgs object from the Selecting event as an argument for my specified Select and SelectCount operations. The behavior for custom paging with the ObjectDataSource involves 2 calls: 1 to get the results of the select operation, and 1 to get the total number of records. What I think sucks about this is that it requires 2 different database calls - or does it? No, it doesn't. Like I said, the code I posted last month (last year too) worked around this issue, but I questioned how much of a hack it was.

I emailed Fritz Onion and asked him to take a look at it and he suggested that it was a good idea to reduce the round trips to the database and offered a much cleaner solution. Instead of including the ObjectDataSourceSelectingEventArgs to set the TotalRowCount property, he suggested simply setting a value in the HttpContext.Current.Items hashtable during the select operation and the use that value in the select count operation. This is possible because HttpContext.Current.Items is a per-request hashtable and both operations are obviously executed during the same request. Thank you Fritz!

Here is the updated code...

<asp:GridView ID="gridViewOrders" runat="server" AllowPaging="True" AutoGenerateColumns="False"

    CellPadding="4" DataSourceID="objectDataSourceOrders" ForeColor="#333333" GridLines="None">

    <FooterStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />

    <Columns>

        <asp:BoundField DataField="ProductId" HeaderText="ProductId" SortExpression="ProductId" />

        <asp:BoundField DataField="ProductName" HeaderText="ProductName" SortExpression="ProductName" />

        <asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice" SortExpression="UnitPrice" />

        <asp:BoundField DataField="CustomerId" HeaderText="CustomerId" SortExpression="CustomerId" />

        <asp:BoundField DataField="OrderId" HeaderText="OrderId" SortExpression="OrderId" />

        <asp:BoundField DataField="OrderDate" HeaderText="OrderDate" SortExpression="OrderDate" />

        <asp:BoundField DataField="ContactName" HeaderText="ContactName" SortExpression="ContactName" />

    </Columns>

    <RowStyle BackColor="#F7F6F3" ForeColor="#333333" />

    <EditRowStyle BackColor="#999999" />

    <SelectedRowStyle BackColor="#E2DED6" Font-Bold="True" ForeColor="#333333" />

    <PagerStyle BackColor="#284775" ForeColor="White" HorizontalAlign="Center" />

    <HeaderStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />

    <AlternatingRowStyle BackColor="White" ForeColor="#284775" />

</asp:GridView>

<asp:ObjectDataSource ID="objectDataSourceOrders" runat="server" EnablePaging="True"

    SelectMethod="Select" TypeName="OrderDataSource"

    SelectCountMethod="SelectCount"></asp:ObjectDataSource>

 

//Lame, but no DAL in this example. :-)

public class OrderDataSource

{

    public OrderDataSource() { }

 

    public int SelectCount()

    {

        return (int)HttpContext.Current.Items["rowCount"];

    }

 

    public OrderCollection Select(int maximumRows, int startRowIndex)

    {

        using (SqlConnection connection = new SqlConnection("Initial Catalog=Northwind;Integrated Security=SSPI;Data Source=."))

        using (SqlCommand command = new SqlCommand("SeanPagerProc", connection))

        {

            connection.Open();

            command.CommandType = CommandType.StoredProcedure;

            command.Parameters.AddWithValue("@pagestart", startRowIndex);

            command.Parameters.AddWithValue("@pagesize", maximumRows);

            command.Parameters.Add(new SqlParameter("@numresults", SqlDbType.Int, 0, ParameterDirection.Output, false, 0, 0, null, DataRowVersion.Default, 0));

            OrderCollection orders = new OrderCollection();

 

            using (SqlDataReader reader = command.ExecuteReader())

            {

                while (reader.Read())

                {

                    Order o = new Order();

                    //missing null checks, has hard-coded column ordinals...it's all bad

                    o.ContactName = reader.GetString(2);

                    o.CustomerId = reader.GetString(1);

                    o.OrderDate = reader.GetDateTime(4);

                    o.OrderId = reader.GetInt32(3);

                    o.ProductId = reader.GetInt32(5);

                    o.ProductName = reader.GetString(6);

                    o.UnitPrice = (float)reader.GetDecimal(7);

                    orders.Add(o);

                }

            }

            HttpContext.Current.Items["rowCount"] = command.Parameters["@numresults"].Value;

            return orders;

        }

    }
}


Digg!

posted on Saturday, January 21, 2006 11:32 AM

Feedback

# re: Update: GridView Custom Paging with ObjectDataSource 2/9/2006 12:59 PM Eric

That completely rocks. Thank you, and thanks Fritz!

# ASP.NET Design Tradeoffs 2/9/2006 3:02 PM TheChaseMan's Frenetic SoapBox

# re: Update: GridView Custom Paging with ObjectDataSource 2/9/2006 9:05 PM Eric

After several hours of trial and error I got a VB version working. Some items to note:

1. If you use select parameters, including the Sort Paramater, you will have to add them to the SelectCount() function even if you don't use them. Otherwise you get the "ObjectDataSource cannot find a generic select function with paramaters x,y".

2. When you do include sorting, the start row index reverts to 0 every time you hit one of the sort column links. Is there a way to disable that?

3. The new SQl Server ROW_NUMBER function works great :)

I wish I understood why you use the Order class a little better; couldn't you just read the data directly into a new collection without it? I'm an Econ major who has become a project management consultant, so you may have to speak in small words :) This is really cool, thanks again for a great post!

# re: Update: GridView Custom Paging with ObjectDataSource 2/10/2006 6:58 PM Sean Chase

>>I wish I understood why you use the Order class a little better; couldn't you just read the data directly into a new collection without it?

Yes, you could...but that's part of why I put in the comment:

//Lame, but no DAL in this example. :-)

To answer your question: typically the process of creating Order objects and placing them into a collection is done in a Data Access layer. The Order class and OrderCollection class would be used as Data Transfer Objects (DTOs) defined in a common assembly referenced by the UI, BL, and DAL layers. I didn't bother to write all of that for the sake of brevity.

Hope that helps,

Sean

# re: Update: GridView Custom Paging with ObjectDataSource 4/4/2006 2:49 AM Dotnetshadow

Hi,

Great articles I do have one problem though that I can't work out. If you want to set the Gridview PagerSettings Mode = NumericFirstLast and you want to set the Gridview.PagerSettings.LastPAgeText to be the numeric value of the last page e.g.
1 ...2 3 4 5 .. 10 <-- LAST PAGE TEXT

I have tried the GridView1_DataBound event and GridView1_RowDataBound to try and set the value as follows:
GridView1.PagerSettings.LastPageText = CType(HttpContext.Current.Items("total"), Integer) / GridView1.PageSize

This works but has one irretating problem that the SelectMethod and SelectCountMethod get called twice. I did a bit of a test and set this setting in the Page_Load such as:
GridView1.PagerSettings.LastPageText = "test" both the SelectMethod and SelectCountMethod only got called once, which is the desired result. The Page_Load is no good because I don't have the number of records by that stage as it fires too early does anyone have a solution?

Regards Dotnetshadow

# re: Update: GridView Custom Paging with ObjectDataSource 4/4/2006 4:58 PM Sean Chase

Maybe try PreRender?

# re: Update: GridView Custom Paging with ObjectDataSource 4/4/2006 6:55 PM DotnetShadow

Yeah I tried that same result :-(

# re: Update: GridView Custom Paging with ObjectDataSource 4/4/2006 10:35 PM Dotnetshadow

Hi,

I narrowed the problem down a bit:

Basically I have a gridview with an objectdatasource. Basically my object has two methods SelectMethod and SelectCount Method. I wanted to set the text for LastPageText as a test I did the following in the Page_Load Event

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

GridView1.PagerSettings.LastPageText = "Last Page1"
GridView1.PagerSettings.LastPageText = "Last Page2"

End Sub

What I found out through debugging was that my SelectMethod and SelectCount Method would be called continuously, it seems that when setting the text it checks the old value and if it's different has to refresh the grid, since the above is in effect a toggle situation then the endless loop occurs.

I tried this test because in my real problem I found that when I changed the text my SelectMethod and SelectCountMethod was called twice on the very first page load... as a result it seems that's because my text differed to the default so it had to refresh it'self hence 2 calls... I hope you can confirm this or suggest a way I could solve this problem

Regards Dotnetshadow

# re: Update: GridView Custom Paging with ObjectDataSource 4/5/2006 9:53 AM Sean Chase

Interesting. I don't have time to mess with it right now because of horrible deadlines, but I'll take a look at it when I get a chance and post something.

# re: Update: GridView Custom Paging with ObjectDataSource 5/10/2006 7:05 AM Boerge

I'm trying to make a custom paging ObjectDataSource, but with an extra select parameter. Do you guys know if that is possible?

Ex:

public List<Item> selectMethod(string Category, int startRowIndex, int maximumRows) {
string sql = "select name, price where category = '" + category + "... }

# re: Update: GridView Custom Paging with ObjectDataSource 10/11/2006 1:32 AM ashish

There is a problem here.
if i do not use objectdatasource then how can i get the virtualcountproperty value.
because when i am paging a gridview and when i go to second page the page button below vanishes.
Please help me regarding this.
--Ashish--

# re: Update: GridView Custom Paging with ObjectDataSource 3/15/2007 1:17 AM Lawrence

Hi, great post, but one little thing:

What if you would not want the select method to fire automaticly when the page loads?
My params come from several controls and are only known when the user has selected those values.
So when my page load I get a "scallar @xxxId not found error".
Can someone plz help me, it's driving me nuts :)

thx

# re: Update: GridView Custom Paging with ObjectDataSource 6/1/2007 2:02 PM Ernesto

I have the same problem that Lawrence. Maybe somebody has an idea?. I need to take parameters from controls to load the grid. Is possible, pass that parameters to the select method?

# VirtualItemCount in Gridview is missing | keyongtech 1/21/2009 8:16 PM Pingback/TrackBack

VirtualItemCount in Gridview is missing | keyongtech

# total items in paged output | keyongtech 1/21/2009 11:47 PM Pingback/TrackBack

total items in paged output | keyongtech