tags:

views:

132

answers:

4

Lets say I have an entity like:

public class Car
{
    public string Make { get; set; }
    public string Model { get; set; }
    public DateTime ReleaseDate { get; set; }
    public int NumCreated { get; set; }
}

We are using a simple MVP architecture for our site with a View Layer (ASP.NET website, Presenter Layer (C# Class Library), Logic Layer (C# Class Library), Data Layer (C# Class Library).

Lets say I have a view that displays a list of cars with sortable column headers. What I've been doing in the past is doing all my sorting in my logic layer, and the view passing an enum for whatever column the list of entities needs to be sorted by.

This is a pain though because I need to maintain an enum for every entity I have and an entry in that enum for every property in the entity.

So, for example, we would be doing something like:

CarLogic.cs

public IEnumerable<Car> GetCarsByYear(string year, SortColumn sortColumn, SortOrder sortOrder)
{
    List<Car> cars = _carRepository.GetCarsByYear(year);  

    switch (sortColumn)
    {
        case SortColumn.Make:
            cars.Sort((car1, car2) =>
                    sortOrder == SortOrder.Ascending
                    ? car1.Make.CompareTo(car2.Make) :
                      car2.Make.CompareTo(car1.Make);
            break;
       case SortColumn.Model:
            cars.Sort((car1, car2) =>
                    sortOrder == SortOrder.Ascending
                    ? car1.Model.CompareTo(car2.Model) :
                      car2.Model.CompareTo(car1.Model);
            break;
       case SortColumn.ReleaseDate:
            cars.Sort((car1, car2) =>
                    sortOrder == SortOrder.Ascending
                    ? car1.ReleaseDate.CompareTo(car2.ReleaseDate) :
                      car2.ReleaseDate.CompareTo(car1.ReleaseDate);
            break;
       case SortColumn.NumCreated:
            cars.Sort((car1, car2) =>
                    sortOrder == SortOrder.Ascending
                    ? car1.NumCreated.CompareTo(car2.NumCreated) :
                      car2.NumCreated.CompareTo(car1.NumCreated);
            break;
       // ...
    }

    return cars;
}

This is how I have been doing it. However, it is a very manual process and if an entity has quite a few properties it can be annoying.

What would be a better way to handle this? Is it possible to allow my collection of entities to be sortable on properties without having to do it all manually per-property like this?

A: 

You can use LINQ to Objects to perform sorting on your entities

var sortedCars = cars.OrderBy(c => c.Model).ThenBy(c => c.Make);

Or alternatively you can use OrderByDescending and ThenByDescending for descedning sorts.

Wallace Breza
Alright. What about the switch statement and the SortOrder and SortColumn enums?
KingNestor
@downvoter - Care to explain why?
Wallace Breza
I downvoted this because it doesn't really seem to have anything to do with his question (the last sentence in his post) at all. Changing the `Sort` to an `OrderBy` doesn't help him remove all the duplicated code for each property.
mquander
+12  A: 

The better way to handle it would be this:

public IEnumerable<Car> GetCarsByYear<T>(string year, SortOrder sortOrder,
    Func<Car, T> fieldSelector) where T : IComparable<T>
{
    List<Car> cars = _carRepository.GetCarsByYear(year);  

    cars.Sort((car1, car2) =>
        sortOrder == sortOrder.Ascending
        ? fieldSelector(car1).CompareTo(fieldSelector(car2)) :
          fieldSelector(car2).CompareTo(fieldSelector(car1)));

    return cars;
}

GetCarsByYear(2010, SortOrder.Ascending,  c => c.Model);
GetCarsByYear(2008, SortOrder.Descending, c => c.Make);

As the poster above mentioned, it would also be a bit more natural to use OrderBy instead of stashing it in a list.

mquander
+1 - Nice touch of sending in the `fieldSelector` as a method parameter.
Wallace Breza
Very nice solution, like it :) +1
spookycoder
A: 

Would it be simpler to sort your collections using IComparer classes?

Alex Peck
A: 

The best method I can think of for this is to use the method

    static List<T> SortList<T>(List<T> list, string column)
    {
        foreach (PropertyInfo p in typeof(T).GetProperties())
        {
            if (p.Name == column)
            {
                return list.OrderBy(c => c.GetType().GetProperty(p.Name).GetValue(c, null)).ToList();
            }
        }
        return list;
    }

This can then be called using

var sortedList = SortList<Car>(cars, "NumCreated");

And since it uses reflection it will work for a list of any object with out having to be changed.

Quinn351
This could easily be changed to take in a enum instead of a string for the column
Quinn351