8

I have a list of objects. How can I order this list using property name?

string orderbyField = "Code";
List<object> l = FillList();
l = l.OrderBy(o => orderbyField);

Can I have an extension for this problem?

DeanOC
  • 6,782
  • 6
  • 40
  • 52
Siamak Ferdos
  • 2,745
  • 2
  • 25
  • 51
  • Can you provide your full code snippet of FillList – Poonam Singhania Oct 30 '14 at 06:23
  • 1
    the concern is you have the list of **object** not particular class so how can you assure that all the object inside the list have the property named as "Code".. better to create the list of specific type.. or having multiple type use interface type list – Deepak Sharma Oct 30 '14 at 06:24
  • I can't have a specific type. FillList return a LINQ entity object. s.t like dc.Employer. I use this list as a datasource of a custom grid in MVC.NET . – Siamak Ferdos Oct 30 '14 at 06:31
  • if you dont have specific type that means you can not assure either that object has the **Code** property or not.. then no sense to create this **OrderBy** LINQ – Deepak Sharma Oct 30 '14 at 06:36
  • But I know property name at runtime(dynamicly). For example on clicking on Code header on grid, I get 'Code' property from object. If there was a way to make something like this: list.OrderBy(o => o.GetType().GetProperty("Code"); – Siamak Ferdos Oct 30 '14 at 06:46

3 Answers3

19

If you don't have to provide the property name as a string, it's pretty simple using dynamic:

List<object> l = FillList();
l = l.OrderBy(o => ((dynamic)o).Id);

If the property name has to be a string, then it gets more a bit complicated but can be done using reflection (although it is not very efficient):

l = l.OrderBy(o => o.GetType()
                    .GetProperty("Code")
                    .GetValue(o, null));

You should also think about adding some error handling, e.g. if the property doesn't exist.

Also, if all the elements in the list have same runtime type, then it would be much more efficient to compile a getter function using expression trees and reusing it (instead of directly using reflection).

public static Func<object, object> CreateGetter(Type runtimeType, string propertyName)
{
    var propertyInfo = runtimeType.GetProperty(propertyName);

    // create a parameter (object obj)
    var obj = Expression.Parameter(typeof(object), "obj");  

    // cast obj to runtimeType
    var objT = Expression.TypeAs(obj, runtimeType); 

    // property accessor
    var property = Expression.Property(objT, propertyInfo); 

    var convert = Expression.TypeAs(property, typeof(object));
    return (Func<object, object>)Expression.Lambda(convert, obj).Compile();
}

and use it like:

var codeGetter = CreateGetter(l[0].GetType(), "Code"); // using the 1st element as an example
l = l.OrderBy(o => codeGetter(o));
Eren Ersönmez
  • 36,276
  • 7
  • 63
  • 88
1

Order by property name without type reflection

    public static class IQueryableExtensions
    {
      public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string 
      propertyName)
      {
      return (IQueryable<T>)OrderBy((IQueryable)source, propertyName);
      }

      public static IQueryable OrderBy(this IQueryable source, string propertyName)
      {
        var x = Expression.Parameter(source.ElementType, "x");
        var body = propertyName.Split('.').Aggregate<string, Expression>(x, 
        Expression.PropertyOrField);

        var selector = Expression.Lambda
         (Expression.PropertyOrField(x, propertyName), x);

         return source.Provider.CreateQuery(
            Expression.Call(typeof(Queryable), "OrderBy", new Type[] { 
             source.ElementType, selector.Body.Type },
                 source.Expression, selector
                 ));
      }

      public static IQueryable<T> OrderByDescending<T>(this IQueryable<T> source, 
      string propertyName)
      {
        return (IQueryable<T>)OrderByDescending((IQueryable)source, propertyName);
      }

      public static IQueryable OrderByDescending(this IQueryable source, string 
      propertyName)
      {
        var x = Expression.Parameter(source.ElementType, "x");
        var selector = Expression.Lambda(Expression.PropertyOrField(x, 
        propertyName),x);
        return source.Provider.CreateQuery(
            Expression.Call(typeof(Queryable), "OrderByDescending", new Type[] { 
             source.ElementType, selector.Body.Type },
                 source.Expression, selector
                 ));
      }
}
Musab.BA
  • 309
  • 5
  • 13
0
    public static IEnumerable<TSource> ComplexOrderBy<TSource>(this IEnumerable<TSource> source, string orderString)
    {
        if (string.IsNullOrWhiteSpace(orderString))
        {
            return source;
        }
        IOrderedEnumerable<TSource> orderedQuery = null;
        var sortingFields = orderString.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
        var propertyNames = typeof(TSource).GetProperties().Select(prop => prop.Name.ToLower()).ToImmutableHashSet();
        for (var i = 0; i < sortingFields.Length; i++)
        {
            var sortingSet = sortingFields[i].Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries);
            var sortBy = sortingSet[0].ToLower();
            var isDescending = sortingSet.Length > 1 && sortingSet[1].Trim().ToLower() == "desc";
            try
            {
                var propertySelector = sortBy.GetPropertySelector<TSource>();
                orderedQuery = isDescending
                                ?
                                (i == 0 ? source.OrderByDescending(propertySelector.Compile()) : orderedQuery.OrderByDescending(propertySelector.Compile()).ThenByDescending(propertySelector.Compile()))
                                :
                                (i == 0 ? source.OrderBy(propertySelector.Compile()) : orderedQuery.ThenBy(propertySelector.Compile()));
            }
            // Just ignoring illegal properties for simplicity
            catch (ArgumentNullException) { }
            catch (ArgumentException) { }
        }
        return orderedQuery ?? source;
    }

    public static Expression<Func<T, object>> GetPropertySelector<T>(this string propertyName)
    {
        var parameterExpression = Expression.Parameter(typeof(T), "x");
        Expression body = parameterExpression;
        foreach (var member in propertyName.Split('.'))
        {
            body = Expression.Property(body, member);
        }
        return Expression.Lambda<Func<T, object>>(Expression.Convert(body, typeof(object)), parameterExpression);
    }
Alexander
  • 739
  • 11
  • 15