8

please consider this scenario:

I have a list of a class with about 50 fields.I want to have a Combobox that user can select according to what field list will sort.For example if user select "F1" list sort according to "F1".

I don't want to sort with if-else for every fields.I see this topic :

Sorting a gridview when databinding a collection or list of objects

but I can't use of it's answer. How I can use Expression Tree for this purpose?

thanks

Edit 1) :

According to dear @Thom Smith answer I wrote this code:

 using (NorthwindModel1.NorthwindEntities2 ent = new NorthwindModel1.NorthwindEntities2())
    {
        var query = from o in ent.Orders
                    where o.OrderID < 10257
                    select o;

        query.OrderBy("CustomerID", SortDirection.Ascending);

        GridView1.DataSource = query;
        GridView1.DataBind();
    }

but it was not sorted. if I wrote that code in this way:

GridView1.DataSource = query.OrderBy(o=>o.CustomerID);

it being sort. where is the problem?

Community
  • 1
  • 1
Arian
  • 12,793
  • 66
  • 176
  • 300

4 Answers4

8

Here's the method I use for this:

private IQueryable<T> OrderQuery<T>(IQueryable<T> query, OrderParameter orderBy)
{
    string orderMethodName = orderBy.Direction == SortDirection.Ascending ? "OrderBy" : "OrderByDescending";
    Type t = typeof(T);

    var param = Expression.Parameter(t, "shipment");
    var property = t.GetProperty(orderBy.Attribute);

    /* We can't just call OrderBy[Descending] with an Expression
     * parameter because the second type argument to OrderBy is not
     * known at compile-time.
     */
    return query.Provider.CreateQuery<T>(
        Expression.Call(
            typeof(Queryable),
            orderMethodName,
            new Type[] { t, property.PropertyType },
            query.Expression,
            Expression.Quote(
                Expression.Lambda(
                    Expression.Property(param, property),
                    param))
        ));
}

OrderParameter is just a struct with an attribute and direction.

EDIT: Additional explanation.

This method is from my DynamicOrderList class, which is a list of OrderParameter objects. If all you need is sorting by one field, then you can simplify it a bit:

private IQueryable<T> OrderByDynamic<T>(this IQueryable<T> query, string attribute, SortDirection direction)
{
    try
    {
        string orderMethodName = direction == SortDirection.Ascending ? "OrderBy" : "OrderByDescending";
        Type t = typeof(T);

        var param = Expression.Parameter(t);
        var property = t.GetProperty(attribute);

        return query.Provider.CreateQuery<T>(
            Expression.Call(
                typeof(Queryable),
                orderMethodName,
                new Type[] { t, property.PropertyType },
                query.Expression,
                Expression.Quote(
                    Expression.Lambda(
                        Expression.Property(param, property),
                        param))
            ));
    }
    catch (Exception) // Probably invalid input, you can catch specifics if you want
    {
        return query; // Return unsorted query
    }
}

Then use it like:

myQuery = myQuery.OrderByDynamic("name", SortDirection.Ascending);

EDIT 2:

public IQueryable<T> OrderBy<T>(this IQueryable<T> query, string attribute, SortDirection direction)
{
    return ApplyOrdering(query, attribute, direction, "OrderBy");
}

public IQueryable<T> ThenBy<T>(this IQueryable<T> query, string attribute, SortDirection direction)
{
    return ApplyOrdering(query, attribute, direction, "ThenBy");
}

private IQueryable<T> ApplyOrdering<T>(IQueryable<T> query, string attribute, SortDirection direction, string orderMethodName)
{
    try
    {
        if (direction == SortDirection.Descending) orderMethodName += "Descending";

        Type t = typeof(T);

        var param = Expression.Parameter(t);
        var property = t.GetProperty(attribute);

        return query.Provider.CreateQuery<T>(
            Expression.Call(
                typeof(Queryable),
                orderMethodName,
                new Type[] { t, property.PropertyType },
                query.Expression,
                Expression.Quote(
                    Expression.Lambda(
                        Expression.Property(param, property),
                        param))
            ));
    }
    catch (Exception) // Probably invalid input, you can catch specifics if you want
    {
        return query; // Return unsorted query
    }
}

And:

myQuery=myQuery.OrderBy("name", SortDirection.Ascending).ThenBy("date", SortDirection.Descending);
Arian
  • 12,793
  • 66
  • 176
  • 300
Thom Smith
  • 13,916
  • 6
  • 45
  • 91
5

OrderBy does not do an in-place sort. It returns a sequence which when evaluated will be sorted. This is usually done lazily, meaning: until it is enumerated, it does nothing. Your current code simply discards this all-important return value. The fix is simple: catch the return value:

query = query.OrderBy("CustomerID", SortDirection.Ascending);

Note: similarly, applying "Where" doesn't filter the existing data: it returns a sequence that when enumerated is filtered. So if you were filtering you'd have the similar:

query = query.Where(...);
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
2

Here's my hot take, using Enumerable+Reflection instead of query:

list.OrderBy(x => {
                       var prop = x.GetType().GetProperty(sortFieldName);
                       return prop.GetValue(x);
                   });

if (!isSortAsc) list.Reverse();
codeMonkey
  • 4,134
  • 2
  • 31
  • 50
  • Even better is to get the 'var prop = x.GetType().GetProperty(sortFieldName);' part outside the lambda expression – Francisco Jan 27 '18 at 05:49
  • A shorter version for this works for older (3.5) framework: list.OrderBy(x => x.GetType().GetProperty(sortFieldName).GetValue(x)); Altough this could, and sould be optimized by putting GetType/GetProperty in an outer variable. – CLS Apr 20 '21 at 14:52
-1

Hope it will be helpful. It worked for Me to filter C# List Dynamically

string jtSorting = "ContactName";
DashboardModel Sup = new DashboardModel();
List<Applicant> lstSup = Sup.GetonBoard();
lstSup = lstSup.AsQueryable().SortBy(jtSorting).ToList();
Just TFS
  • 4,697
  • 1
  • 18
  • 22
raji
  • 1