Introduction
This is a continuation of the update to my previous article, Generically Populate List of Objects from SqlDataReader. The information posted in this tip is intended primarily to add the ability to sort a list of objects based on a given property within the class.
Background
In the 'parent' article to this, I created a generic routine that will populate a list of objects from a SqlDataReader
object using reflection to dynamically create a lambda expression to create instances of a class and populate them from the SqlDataReader
. This worked well, but in using that list as a data source for my various data views, I quickly found there was something lacking -- the ability to quickly sort the list based on a property within the class.
We often allow the sorting of the list by clicking on the column header within whatever control we are using to display data. So, the requirements for the sorting became:
- Use the models within C# to allow generic ('
<T>
') functionality
- Sorting should allow for sorting by a single property of the class
- Sorting should allow for both ascending or descending order
For the purposes of this article, I'm using the Employee
table within the Northwind SQL Server sample database. Apart from creating the database, all of the code is available in the download package.
Using the ListHelper Class to Sort
There are basically three steps necessary:
- Create a data model that represents an individual item in the list
- Populate the list using the
CreateList()
function
- Sort the list using the
SortList()
function
Here is my data model for the Employee
. As you can see, there is nothing special about the data model. It does allow for various data types to be tested, as well as nullable data types.
[Serializable]
public class Employee
{
public int EmployeeID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string Title { get; set; }
public DateTime? BirthDate { get; set; }
public DateTime? HireDate { get; set; }
public string City { get; set; }
}
Executing the query and populating a list based on this data model is really quite simple. Once the list is populated, you can sort it quite easily.
var query = "select * from Employees order by LastName, FirstName";
var cmd = new SqlCommand(query, conn);
using (var reader = cmd.ExecuteReader())
{
var listHelper = new GenericPopulator.ListHelper<Employee>();
var employeeList = listHelper.CreateList(reader);
employeeList = listHelper.SortList(employeeList, "LastName", true);
rptEmployees.DataSource = employeeList;
rptEmployees.DataBind();
reader.Close();
}
Explanation of the SortList() Function
In order to dynamically sort a list based on a data model, you must use Reflection and dynamically generate a lambda expression. From our example above, attempting to sort based on the LastName
property of the Employee
class would require the following LINQ statement:
employeeList = employeeList.AsQueryable().OrderBy(e => e.LastName).ToList();
The SortList()
function will dynamically generate the lambda expression to use. Once the dynamic lambda is generated, it will sort the list using simple LINQ functions. Unfortunately, the nature of LINQ means that you must call a separate function to sort in ascending versus descending order.
public List<T> SortList(List<T> listToSort, string propertyName, bool ascending)
{
var propertyNames = typeof(T).GetProperties().ToList().Select(p => p.Name).ToList();
if (!propertyNames.Contains(propertyName))
throw new ArgumentOutOfRangeException("There is no property named: " + propertyName);
var paramExpression = Expression.Parameter(typeof(T), "item");
var propertyExpression = Expression.Convert
(Expression.Property(paramExpression, propertyName), typeof(object));
var lambdaExpression = Expression.Lambda<Func<T,
object>>(propertyExpression, paramExpression);
if (ascending)
return listToSort.AsQueryable().OrderBy(lambdaExpression).ToList();
else
return listToSort.AsQueryable().OrderByDescending(lambdaExpression).ToList();
}
Points of Interest
Eat My Own Dog Food
Please understand that, while there was some research and experimentation for this project, the result was useful. This method of generically populating and sorting lists is used within my company on a regular basis now.
Download Package
Inside the download package, you will find the complete, usable code for the functionality described here.
Custom Attributes
This version allows for custom attributes on the properties of the data model. These custom attributes are basically present to allow you to have different property names and column names (EmployeeID
property populated from Employee_ID
column).