C# - Code to Order by a Property Using the Property Name as a String

C# - code to order by a property using the property name as a string

I would offer this alternative to what everyone else has posted.

System.Reflection.PropertyInfo prop = typeof(YourType).GetProperty("PropertyName");

query = query.OrderBy(x => prop.GetValue(x, null));

This avoids repeated calls to the reflection API for obtaining the property. Now the only repeated call is obtaining the value.

However

I would advocate using a PropertyDescriptor instead, as this will allow for custom TypeDescriptors to be assigned to your type, making it possible to have lightweight operations for retrieving properties and values. In the absence of a custom descriptor it will fall back to reflection anyhow.

PropertyDescriptor prop = TypeDescriptor.GetProperties(typeof(YourType)).Find("PropertyName");

query = query.OrderBy(x => prop.GetValue(x));

As for speeding it up, check out Marc Gravel's HyperDescriptor project on CodeProject. I've used this with great success; it's a life saver for high-performance data binding and dynamic property operations on business objects.

Order a list by a property name(string value)?

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));

Sorting using property name as string

The solution from your link uses an "Expression.Convert" which most of the time doesn't work with LINQ to Entities.

Here is a working extension method:

public static IOrderedQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, string propertyName)
{
// LAMBDA: x => x.[PropertyName]
var parameter = Expression.Parameter(typeof(TSource), "x");
Expression property = Expression.Property(parameter, propertyName);
var lambda = Expression.Lambda(property, parameter);

// REFLECTION: source.OrderBy(x => x.Property)
var orderByMethod = typeof(Queryable).GetMethods().First(x => x.Name == "OrderBy" && x.GetParameters().Length == 2);
var orderByGeneric = orderByMethod.MakeGenericMethod(typeof(TSource), property.Type);
var result = orderByGeneric.Invoke(null, new object[] { source, lambda });

return (IOrderedQueryable<TSource>)result;
}

Disclaimer: I'm the owner of the project EF+ on GitHub.

You can find other methods to order by property name in my repository: GitHub

  • OrderByDescending
  • ThenBy
  • ThenByDescending
  • AddOrAppendOrderBy
  • AddOrAppendOrderByDescending

EDIT: Answer sub-question

Is it possibly to sort by navigation properties using something like
this, e.g. a property name "NavigationProperty.PropertyName"

Yes, you can either split the string and loop to create the expression with the property path or use a real expression evaluator.

Disclaimer: I'm the owner of the project Eval-Expressions.NET

This library allows you to execute all LINQ method dynamically.

See: LINQ Dynamic

var result = list.OrderByDynamic(x => "NavigationProperty.PropertyName");

How to give Property Name in linq orderBy based on string Property name in C#

The second last of your examples is actually quite close to something that should work.

Ignoring everything around it the following should be a working version of the mentioned example:

// Used reflection 
var convertProperty = typeof(Student).GetProperty(columnname);
students.OrderByDescending(n => convertProperty.GetValue(n).ToString() ?? string.Empty).ToListAsync();

To be fair, I haven't tried out this code, so I might have made a mistake somewhere, given this is typed freely without an IDE, but it should give a general Idea, right?

Edit

If you are using C# 6.0 and upwards you can use null checking like mentioned above, otherwise you can also use the following

students.OrderByDescending(n => (convertProperty.GetValue(n) ?? string.Empty).ToString()).ToListAsync();

C# Sort ListT by its property which is string of datetime

Based on your comments on the answers you are looking for a reflection based solution.

The reflection part, to get the value of a property based on the name:

x.GetType().GetProperty(propertyName).GetValue(x, null)

Try this:

var propetyName = "StringAsDate";
var listSorted = list.OrderByDescending(x => DateTime.Parse(Convert.ToString(x.GetType().GetProperty(propertyName).GetValue(x, null))));

If the Property is already of type DateTime a cast would be sufficient

var propetyName = "StringAsDate";
var listSorted = list.OrderByDescending(x => (DateTime)x.GetType().GetProperty(propertyName).GetValue(x, null));

Sort List of C# object by STRING parameter

Add reference to nuget package:

https://www.nuget.org/packages/System.Linq.Dynamic/

  1. Add using System.Linq.Dynamic; at the top.
  2. Use var usersSorted = users.AsQueryable().OrderBy("firstname ASC").ToList();

How to Sort a ListT by a property in the object

The easiest way I can think of is to use Linq:

List<Order> SortedList = objListOrder.OrderBy(o=>o.OrderDate).ToList();

Order By String which matches property name gives Null Reference

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
));
}
}

then you can use it like this

var people = DbContext.People
.Select(p => new PersonViewModel{
Name = p.Name,
ManagerId = p.ManagerId,
DepartmentId = p.DepartmentId
})
.OrderBy(paging.Sorting)

or you can do it with type reflection (without IQueryableExtensions) (although it is not very efficient): like this

var people = DbContext.People
.Select(p => new PersonViewModel{
Name = p.Name,
ManagerId = p.ManagerId,
DepartmentId = p.DepartmentId
})
.OrderBy(o => o.GetType()
.GetProperty(paging.Sorting)
.GetValue(o, null))

Ordering by ICollection property using its name as string with Dynamic Linq Library

You can replace the t => t.table2.Select(t2 => t2.field).FirstOrDefault() with a supported function, Min():

OrderBy("table2.Min(field)");


Related Topics



Leave a reply



Submit