Динамическая сортировка в 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);
Ответ 3
Посмотрите эту ссылку Множественная сортировка полей по именам полей с помощью Linq
Надеюсь, что это поможет вам в достижении того, что вам нужно.
Ответ 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();