linq Order By for a List(Of myObjects)
Solution 1
VB
Module OrderByExtensions
<System.Runtime.CompilerServices.Extension()> _
Public Function OrderByPropertyName(Of T)(ByVal e As IEnumerable(Of T), ByVal propertyName As String) As IOrderedEnumerable(Of T)
Dim itemType = GetType(T)
Dim prop = itemType.GetProperty(propertyName)
If prop Is Nothing Then Throw New ArgumentException("Object does not have the specified property")
Dim propType = prop.PropertyType
Dim funcType = GetType(Func(Of ,)).MakeGenericType(itemType, propType)
Dim parameter = Expression.Parameter(itemType, "item")
Dim exp = Expression.Lambda(funcType, Expression.MakeMemberAccess(parameter, prop), parameter)
Dim params = New Object() {e, exp.Compile()}
Return DirectCast(GetType(OrderByExtensions).GetMethod("InvokeOrderBy", Reflection.BindingFlags.Static Or Reflection.BindingFlags.NonPublic).MakeGenericMethod(itemType, propType).Invoke(Nothing, params), IOrderedEnumerable(Of T))
End Function
Private Function InvokeOrderBy(Of T, U)(ByVal e As IEnumerable(Of T), ByVal f As Func(Of T, U)) As IOrderedEnumerable(Of T)
Return Enumerable.OrderBy(e, f)
End Function
End Module
C#
public static class OrderByExtensions
{
public static IOrderedEnumerable<T> OrderByPropertyName<T>(this IEnumerable<T> e, string name)
{
var itemType = typeof(T);
var prop = itemType.GetProperty(name);
if (prop == null) throw new ArgumentException("Object does not have the specified property");
var propType = prop.PropertyType;
var funcType = typeof(Func<,>).MakeGenericType(itemType, propType);
var parameter = Expression.Parameter(itemType, "item");
var memberAccess = Expression.MakeMemberAccess(parameter, prop);
var expression = Expression.Lambda(funcType, memberAccess, parameter);
var x = typeof(OrderByExtensions).GetMethod("InvokeOrderBy", BindingFlags.Static | BindingFlags.NonPublic);
return (IOrderedEnumerable<T>)x.MakeGenericMethod(itemType, propType).Invoke(null, new object[] { e, expression.Compile() });
}
static IOrderedEnumerable<T> InvokeOrderBy<T, U>(IEnumerable<T> e, Func<T, U> f)
{
return e.OrderBy(f);
}
}
Solution 2
Pass the sort column as an Function.
So it would be
public SomeList Foo(Function<Foo, bool> sortFunction, int skip, int PageSize)
{
return returnReports.OrderBy(sortFunction).Skip(skip).Take(PageSize).ToList();
}
Call it like this
SomeList(f => f.Bar, 5, 10);
Solution 3
if you're working with a database as a source of data then you can use Dynamic LINQ project that allows you to specify parameters to the Where clause as a string.
If you're working with "Linq to objects" than you'll need to create the lambda function that is passed as an argument dynamically. You can do this by using the "Expression.Xyz" methods to build the expression tree and then using the "Compile" method that turns the expression tree into a callable delegate (of type Func<>) that you can use as an argument to Where. An example how to construct the expression tree can be found in another SO thread here.
Solution 4
You can't do it (easily) if you're just passed a string. You could have a map from String to Func<IEnumerable<Report>, IEnumerable<Report>>
, e.g. (in C#)
// Horrible type. Ick.
private static readonly
Dictionary<string, Func<IEnumerable<Report>,IEnumerable<Report>>>
Orderings =
new Dictionary<string, Func<IEnumerable<Report>,IEnumerable<Report>>>
{
{ "FirstColumn", (IEnumerable<Report> reports) =>
reports.OrderBy(report => report.FirstColumn) },
{ "SecondColumn", (IEnumerable<Report> reports) =>
reports.OrderBy(report => report.SecondColumn) },
(etc)
};
Then use:
// For production usage, include some error checking!
return Orderings[sortColumn].Skip(skip).Take(pageSize).ToList();
If you can get SortColumn
to be passed in as an appropriate Func
(possibly by making your method generic) that would avoid the mess here.
Slee
Updated on June 14, 2022Comments
-
Slee almost 2 years
How do I order by a passed string value on my list of objects? i need to do paging and sorting on my List(Of) objects the paging is no problem but I don;t know who to get the Order By to work.
Here is what I am currently doing and it's working great:
Return returnReports.Skip(PageSize * (PageNumber-1)).Take(PageSize).ToList()
How do I get this to work?
Return returnReports.OrderBy(SortColumn).Skip(skip).Take(PageSize).ToList()
SortColumn being a passed string value
-
mmx about 15 yearsAction<T> delegate does not return a value.
-
mmx about 15 years@Richard: It's funny cause I've been struggling to make sure the VB one is correct (as a C# guy) that I missed
this
while translating to C#. -
Slee about 15 yearsthis is great - I'll have to study this. One more thing, how would I make this so I could say if i wanted it ascending or descending in order?
-
mmx about 15 yearsJust copy and paste it and do a replace all OrderBy -> OrderByDescending. By the way, this is not a very performant method as it has to use reflection and build the expression dynamically. If you know the type you're working with, go with Jon's solution.