LINQ order by null столбец, где порядок по возрастанию и нули должны быть последними


Я пытаюсь отсортировать список товаров по их цене.

результирующий набор должен перечислять продукты по цене от низкой до высокой по столбцу LowestPrice. Однако этот столбец может быть обнулен.

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

var products = from p in _context.Products
   where p.ProductTypeId == 1
   orderby p.LowestPrice.HasValue descending
   orderby p.LowestPrice descending
   select p;

// returns:    102, 101, 100, null, null

однако я не могу понять, как сортировать это в порядке возрастания.

// i'd like: 100, 101, 102, null, null
8 94

8 ответов:

Попробуйте поместить оба столбца в один и тот же порядок.

orderby p.LowestPrice.HasValue descending, p.LowestPrice

в противном случае каждый orderby-это отдельная операция по переупорядочиванию коллекции каждый раз.

Это должно упорядочить те, с A со значением сначала," затем " порядок значения.

это действительно помогает понять синтаксис запроса LINQ и как он переводится на вызовы метода LINQ.

получается, что

var products = from p in _context.Products
               where p.ProductTypeId == 1
               orderby p.LowestPrice.HasValue descending
               orderby p.LowestPrice descending
               select p;

будет переведен компилятором в

var products = _context.Products
                       .Where(p => p.ProductTypeId == 1)
                       .OrderByDescending(p => p.LowestPrice.HasValue)
                       .OrderByDescending(p => p.LowestPrice)
                       .Select(p => p);

это, безусловно, не то, что вы хотите. Это сортирует по Product.LowestPrice.HasValue на descending заказать, а затем повторно сортирует всю коллекцию по Product.LowestPrice на descending порядок.

то, что вы хотите, это

var products = _context.Products
                       .Where(p => p.ProductTypeId == 1)
                       .OrderByDescending(p => p.LowestPrice.HasValue)
                       .ThenBy(p => p.LowestPrice)
                       .Select(p => p);

который можно получить с помощью синтаксиса запроса на

var products = from p in _context.Products
               where p.ProductTypeId == 1
               orderby p.LowestPrice.HasValue descending,
                       p.LowestPrice
               select p;

подробные сведения о переводах из синтаксиса запросов в вызовы методов см. В спецификации языка. Серьезно. Прочитать его.

У меня есть другой вариант в этой ситуации. Мой список objList, и я должен заказать, но нули должны быть в конце. мое решение:

var newList = objList.Where(m=>m.Column != null)
                     .OrderBy(m => m.Column)
                     .Concat(objList.where(m=>m.Column == null));

решение для строковых значений действительно странно:

.OrderBy(f => f.SomeString == null).ThenBy(f => f.SomeString) 

единственная причина, которая работает, потому что первое выражение, OrderBy() вроде bool значения: true/false. false результат идти первым после true результат (nullables) и ThenBy() сортировка ненулевых значений в алфавитном порядке.

Итак, я предпочитаю делать что-то более читаемое, например:

.OrderBy(f => f.SomeString ?? "z")

если SomeString равно null, он будет заменен на "z" и затем сортировать все в алфавитном порядке.

Примечание: это не окончательное решение, так как "z" идет первым, чем z-значения, такие как zebra.

обновление 9/6/2016-о комментарии @jornhd, это действительно хорошее решение, но оно все еще немного сложное, поэтому я рекомендую обернуть его в класс расширения, например:

public static class MyExtensions
{
    public static IOrderedEnumerable<T> NullableOrderBy<T>(this IEnumerable<T> list, Func<T, string> keySelector)
    {
        return list.OrderBy(v => keySelector(v) != null ? 0 : 1).ThenBy(keySelector);
    }
}

и просто использовать его как:

var sortedList = list.NullableOrderBy(f => f.SomeString);

мое решение:

Array = _context.Products.OrderByDescending(p => p.Val ?? float.MinValue)

это то, что я придумал, потому что я использую методы расширения, а также мой элемент является строкой, поэтому нет .HasValue:

.OrderBy(f => f.SomeString == null).ThenBy(f => f.SomeString)

это работает с объектами LINQ 2 в памяти. Я не тестировал его с помощью EF или любого DB ORM.

Я пытался найти решение LINQ для этого, но не получается из ответов здесь.

мой окончательный ответ был:

.OrderByDescending(p.LowestPrice.HasValue).ThenBy(p.LowestPrice)

ниже есть метод расширения, чтобы проверить на null, если вы хотите отсортировать на собственность ребенка в качестве параметра keyselector.

public static IOrderedEnumerable<T> NullableOrderBy<T>(this IEnumerable<T> list, Func<T, object> parentKeySelector, Func<T, object> childKeySelector)
{
    return list.OrderBy(v => parentKeySelector(v) != null ? 0 : 1).ThenBy(childKeySelector);
}

и просто использовать его как:

var sortedList = list.NullableOrderBy(x => x.someObject, y => y.someObject?.someProperty);