Practical ASP.NET

Sorting in the ObjectDataSource

Your users may want to get their data in a specific order -- and not get it all at once. You can control both with the ObjectDataSource (and a little code).

Databinding to tables in single-tier applications has always been easy. ASP.NET's ObjectDataSource does something wonderful: It makes databinding to business objects in n-tier applications just as easy.

Letting users sort a GridView in single-tier databinding applications isn't hard: Just turn on the option in the GridView's SmartTag. You can also let your users sort the rows in a GridView when using the ObjectDataSource -- it only requires a little more work.

Enabling Sorting
As is often the case, turning on functionality in a DataSource and a DataView involves making changes to both controls. For instance, when working with an ObjectDataSource, the Enable Sorting option won't appear in the GridView's SmartTag until you turn on the option in the ObjectDataSource. The option to set isn't obvious: In the Properties window for the ObjectDataSource, you must set the SortParameterName to some string. Once you do that, you'll find that the SmartTag for the GridView has an Enable Sorting option that you can check off.


However, turning on the sort option just enables the user interface support (the column names in the GridView become hyperlinks that the user can click on to sort the rows in the GridView by that column). The actual work of doing the sort is turned over to the factory method of the middle-tier business object so you have to make changes there also. For instance, if you've set the SortParameterName to, say, "MySortExpression," then the factory method that your ObjectDataSource calls must have a parameter called MySortExpression.

After you've set the SortParameterName on the ObjectDataSource and enabled sorting on the GridView, when the user clicks on a column header the value of the column's SortExpression will be passed to the factory method specified in the ObjectDataSource. The default value for each column's SortExpression is the name of the field that the column is bound to.

However, you can change the SortExpression for any column in the Edit Columns dialog. For instance, in a GridView displaying a list of Customer objects, you might want to set the LastName column's SortExpression to "LastName, FirstName." This would position you so that when a user clicks on the LastName column header, you can do a "phone book sort": sort by LastName and FirstName. You can also include the SQL keyword for a descending sort -- for the customer's BirthDate column you might set the SortExpression to "BirthDate DESC."

After configuring the GridView, you can turn your attention to making the changes in your factory method. In addition to whatever parameters you need to retrieve the data for your object, you need that additional string parameter with the name you used in the SortParameterName.

This example accepts an integer parameter called OrderId and a string parameter called MySortExpression. The OrderId parameter is used to retrieve the matching OrderDetail records from the Northwind database. MySortExpression will hold the SortExpression passed from the GridView. If you've been following my suggestions for setting a column's SortExpression, it can be used in the Order By clause of a SQL statement that retrieves your data.

A typical factory method that uses both parameters to create a list of OrderDetail objects would look like this:

Public Shared Function SelectOrderDetailsByOrderId( _
ByVal orderId As Integer, ByVal MySortExpression As String) _
   As List(Of OrderDetail)

Using conn As New System.Data.SqlClient.SqlConnection(connectionString)
            conn.Open()
    Dim SqlStatement As String = _
                  "SELECT * FROM [Order Details] WHERE OrderId = @OrderId"
    Dim comm As System.Data.SqlClient.SqlCommand
    If SortExpression > "" Then
       comm = New System.Data.SqlClient.SqlCommand(SqlStatement & _
                             " Order By " & SortExpression, conn)
    Else
       comm = New System.Data.SqlClient.SqlCommand(SqlStatement, conn)
    End If

    comm.Parameters.Add("@OrderId", Data.SqlDbType.Int).Value = orderId
    Using reader As System.Data.SqlClient.SqlDataReader = _
          comm.ExecuteReader(Data.CommandBehavior.SingleResult And _  
          Data.CommandBehavior.CloseConnection)
       Dim ods As New List(Of OrderDetail)
       Dim RecCount As Integer

       While reader.Read()
           Dim od As OrderDetail = CreateOrderDetailFromReader(reader)
           ods.Add(od)
       End While
       Return ods
    End Using
  End Using
End Function


About the Author

Peter Vogel is a principal in PH&V Information Services, specializing in ASP.NET development with expertise in SOA, XML, database, and user interface design. His most recent book ("rtfm*") is on writing effective user manuals, and his blog on technical writing can be found at rtfmphvis.blogspot.com.

Reader Comments:

Mon, Aug 24, 2009 Peter Vogel Canada

I did return to this topic to address the issues that Richard raised. You can find the followup column at http://visualstudiomagazine.com/articles/2009/08/06/extend-sorting-in-the-gridview.aspx

Wed, Jul 22, 2009 Peter Vogel Canada

Richard is right: if you include a direction in the SortExpression column (like DESC) then your application will blow up when the user clicks on the column a second time and the GridView attempts to do a descending sort on the column. This is because the GridView just blindly adds DESC to whatever's in the SortExpression property when doing a descending sort. This also leads to the problem with specifying multiple columns in the SortExpression (as Richard implies):If you put "LastName, FirstName" in the SortExpression then the second click on the column will cause the GridView to generate "LastName, FirstName DESC"-- still sorting LastName in ascending order. You could, presumably, catch the Sorting event, check the SortDirection property on the e parameter, and (in the event) rewrite your SortExpression to "LastName DESC, FirstName DESC" when SortDirection is doing a descending sort on the column (I've never tried it)--I'll look at this in a later column. I'd be inclined, however, to see if you could just get a new property added to the object that returns the concatenated values (a FullName property in this case) and sort on that. But then I'm pretty lazy.

Wed, Jul 22, 2009 Peter Vogel Canada

I've always wanted to do a test and see if SortExpression > "" really does run faster than Len(SortExpression) > 0. In the old VB days, all strings began with a byte that contained their length which is what the Len function retrieved--and did it very quickly, too (nowadays, I would use SortExpression.Length = 0 rather than the Len function--but I suspect that it doesn't make a difference because I bet that the Len function just calls the Length property). But I wonder if the cost of comparing a single string byte is much different from comparing a single integer in .NET. I wouldn't be surprised to find that the compiler may even spot the > "" test and swap in some optimized solution. I use SortExpression > "" because it says what I'm doing: Checking to see if the SortExpression string contains anything. I do consider switching over to SortExpression Is Empty (or whatever) but so far I've been too lazy.

Mon, Jul 20, 2009 Richard

If you specify a sort expression of "BirthDate DESC" and then sort the column in descending order, the sort parameter will receive "BirthDate DESC DESC".

Similarly, if you specify "LastName, FirstName" and sort in descending order, you will get "LastName, FirstName DESC", not the expected "LastName DESC, FirstName DESC".

Fri, Jul 17, 2009

This is a great article thank you! Is there any way to do muti-column sorting on the gridview when wired up to an Object Data Source 'without' calling the database again? In a nutshell, can the ODS be re-sorted and re-bound to the grid based on multiple columns without having to send the sort expression all the way down to the DAL for a new set of data?

Fri, Jul 17, 2009 Paul Chouinard

Instead of evaluating the string expression SortExpression > "", it is generally preferable to evaluate its length as in Len(SortExpression) > 0

Add Your Comments Now:

Your Name:(optional)
Your Email:(optional)
Your Location:(optional)
Comment:
Please type the letters/numbers you see above