Динамическая сортировка в linq

рассмотрите этот сценарий:

У меня есть список классов с примерно 50 полями. Я хочу иметь Combobox, который пользователь может выбрать в соответствии с тем, какой список полей будет сортироваться. Например, если пользователь выбирает список "F1" в соответствии с "F1".

Я не хочу сортировать с if-else для каждого поля. Я вижу этот раздел:

Сортировка gridview при привязке данных к коллекции или списку объектов

но я не могу использовать его. Как я могу использовать Expression Tree для этой цели?

спасибо

Изменить 1):

По словам уважаемого @Thom Smith, я написал этот код:

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

но он не был отсортирован. если я написал этот код следующим образом:

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

это сортировка. где проблема?

Ответ 1

OrderBy не выполняет сортировку на месте. Он возвращает последовательность, которая при оценке будет сортироваться. Обычно это делается лениво, что означает: до тех пор, пока оно не будет перечислено, оно ничего. Ваш текущий код просто отбрасывает это важное значение. Исправить это просто: поймайте возвращаемое значение:

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

Примечание. Аналогичным образом применение "Где" не фильтрует существующие данные: оно возвращает последовательность, которая при фильтрации перечисляется. Поэтому, если вы фильтруете, у вас будет аналогичное:

query = query.Where(...);

Ответ 2

Здесь метод, который я использую для этого:

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 - это просто структура с атрибутом и направлением.

EDIT: дополнительное объяснение.

Этот метод из моего класса DynamicOrderList, который представляет собой список объектов OrderParameter. Если все, что вам нужно, сортирует по одному полю, вы можете немного упростить его:

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

Затем используйте его как:

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

ИЗМЕНИТЬ 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
    }
}

и

myQuery=myQuery.OrderBy("name", SortDirection.Ascending).ThenBy("date", SortDirection.Descending);

Ответ 4

Здесь мой горячий прием, используя Enumerable + Reflection вместо запроса:

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

if (!isSortAsc) list.Reverse();

Ответ 5

Надеюсь, это будет полезно. Он работал у меня, чтобы фильтровать список С# Dynamicically

string jtSorting = "ContactName";
DashboardModel Sup = new DashboardModel();
List<Applicant> lstSup = Sup.GetonBoard();
lstSup = lstSup.AsQueryable().SortBy(jtSorting).ToList();