С# Linq OrderBy для фильтрации нулевых или пустых значений в последний раз
Я пытаюсь сделать свой заказный метод расширения, я успешно сработал свой код, но, кроме того, я хочу, чтобы список нулевых или пустых или нулевых значений был последним в результате, кто-нибудь может помочь мне в этой проблеме?
Вот мой метод расширения для orderby
public static IQueryable<T> OrderBy<T>(this IQueryable<T> q, string SortField, bool isAsc)
{
//var nullExpr = Expression.Constant(null, typeof(T));
var param = Expression.Parameter(typeof(T), "p");
var prop = Expression.Property(param, SortField);
var exp = Expression.Lambda(prop, param);
string method = isAsc ? "OrderBy" : "OrderByDescending";
Type[] types = new Type[] { q.ElementType, exp.Body.Type };
var mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp);
return q.Provider.CreateQuery<T>(mce);
}
Заранее спасибо
Ответы
Ответ 1
Без использования метода расширения....
Создайте пользовательский IComparer<string>
, чтобы проверить пустые значения перед использованием String.Compare
по умолчанию. Первые проверки возвращают -1 вместо 1 или 1 вместо -1, если использовать стандартное сравнение строк.
/// <summary>
/// Returns -1 instead of 1 if y is IsNullOrEmpty when x is Not.
/// </summary>
public class EmptyStringsAreLast : IComparer<string>
{
public int Compare(string x, string y)
{
if (String.IsNullOrEmpty(y) && !String.IsNullOrEmpty(x))
{
return -1;
}
else if (!String.IsNullOrEmpty(y) && String.IsNullOrEmpty(x))
{
return 1;
}
else
{
return String.Compare(x, y);
}
}
}
Передайте ваш EmptyStringsAreLast
-рекордер в OrderBy
выражения Lambda. В этом решении команды, которые вошли в гонку, должны появиться в алфавитном порядке, но после этого должны появиться неаффилированные записи гонки.
var entries = repository.Race.Where(e => e.EventId == id)
.OrderBy(e => e.TeamName, new EmptyStringsAreLast())
.ThenBy(e => e.LastName)
.ThenBy(e => e.FirstName);
Ответ 2
Самый простой способ - использовать
OrderBy(e => String.IsNullOrEmpty(e.TeamName)
Это не требует какого-либо метода расширения или пользовательской реализации IComparer
и т.д.
var entries = repository.Race.Where(e => e.EventId == id)
.OrderBy(e => String.IsNullOrEmpty(e.TeamName))
.ThenBy(e => e.LastName)
.ThenBy(e => e.FirstName);
Ответ 3
Этот ответ, возможно, вы изначально искали - с помощью вашего общего метода расширения:
public static IQueryable<T> OrderByFieldNullsLast<T>(this IQueryable<T> q, string SortField, bool Ascending)
{
//We are rebuilding .OrderByDescending(p => p.SortField.HasValue).ThenBy(p => p.SortField)
//i.e. sort first by whether sortfield has a value, then by sortfield asc or sortfield desc
//create the expression tree that represents the generic parameter to the predicate
var param = Expression.Parameter(typeof(T), "p");
//create an expression tree that represents the expression p=>p.SortField.HasValue
var prop = Expression.Property(param, SortField);
var hasValue = Expression.Property(prop, "HasValue");
var exp = Expression.Lambda(hasValue, param);
string method = "OrderByDescending";
Type[] types = new Type[] { q.ElementType, exp.Body.Type };
var orderByCallExpression = Expression.Call(typeof(Queryable), method, types, q.Expression, exp);
//now do the ThenBy bit,sending in the above expression to the Expression.Call
exp = Expression.Lambda(prop, param);
types = new Type[] { q.ElementType, exp.Body.Type };
method = Ascending ? "ThenBy" : "ThenByDescending";
var ThenByCallExpression = Expression.Call(typeof(Queryable), method, types,orderByCallExpression, exp);
return q.Provider.CreateQuery<T>(ThenByCallExpression);
}
Ответ 4
На основе ответа Дейва Ансона, вы можете Comparer.Create(), чтобы создать Comparer из лямбда. Вот пример, который сортирует unsorted
по его строковым полям myString
, с null
или пустыми строками, появляющимися последними.
var sorted = unsorted.OrderBy(x => x.myString, Comparer<string>.Create((x, y) => {
if ( string.IsNullOrEmpty(y) && !string.IsNullOrEmpty(x)) return -1;
else if (!string.IsNullOrEmpty(y) && string.IsNullOrEmpty(x)) return +1;
else return string.Compare(x, y);
}))
(Чтобы поместить их сначала, переключите знаки в константах 1
)
Ответ 5
он работает для меня:
private static IQueryable<T> GetOrderQuery<T>(this IQueryable<T> q, BaseFilterCollection filter)
{
q = q.OrderBy(GetExpression<T>(filter.SortField));
var param = Expression.Parameter(typeof(T), "p");
var prop = Expression.Property(param, filter.SortField);
var exp = Expression.Lambda(prop, param);
string method = filter.SortDirection == SortDirectionType.Asc ? "ThenBy" : "ThenByDescending";
Type[] types = { q.ElementType, exp.Body.Type };
var rs = Expression.Call(typeof(Queryable), method, types, q.Expression, exp);
return q.Provider.CreateQuery<T>(rs);
}
private static Expression<Func<T, bool>> GetExpression<T>(string sortField)
{
ParameterExpression param = Expression.Parameter(typeof(T), "p");
Expression prop = Expression.Property(param, sortField);
var info = typeof(T).GetProperty(sortField, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
Expression exp = Expression.Equal(prop, info.PropertyType.IsValueType
? Expression.Constant(Activator.CreateInstance(info.PropertyType))
: Expression.Constant(null));
return Expression.Lambda<Func<T, bool>>(exp, param);
}