Câu hỏi Nhiều “thứ tự bởi” trong LINQ


Tôi có hai bàn, movies và categoriesvà tôi nhận được danh sách theo thứ tự Thể loại ID đầu tiên và sau đó bởi Tên.

Bảng phim có ba cột, ID, Tên và CategoryID. Bảng danh mục hai có các cột, ID và Tên.

Tôi đã thử một cái gì đó như sau, nhưng nó không hoạt động.

var movies = _db.Movies.OrderBy( m => { m.CategoryID, m.Name })

1383
2017-11-18 13:34


gốc


Đây là lý do tại sao điều này không thể làm việc: Biểu thức lambda trong ngoặc đơn được cho là trả về một giá trị có thể được sử dụng để sắp xếp các mục: m.CategoryID là một số có thể được sử dụng để sắp xếp các mục. Nhưng "m.CategoryID, m.Name" không có ý nghĩa trong ngữ cảnh này. - chiccodoro
bao gồm System.Linq; :)
Hãy xem Truy vấn SQL trong LINQ: sắp xếp theo. Nó giải thích cách dịch các truy vấn SQL phổ biến thành cú pháp LINQ. Một trong những ví dụ bao gồm đặt hàng trên nhiều cột. - Steven Wexler
.ThenBy là những gì bạn đang tìm kiếm? - eka808
Nếu có cơ hội bạn muốn sắp xếp chúng theo thứ tự giảm dần ở đó đây là con đường để đi. - RBT


Các câu trả lời:


Điều này sẽ làm việc cho bạn:

var movies = _db.Movies.OrderBy(c => c.Category).ThenBy(n => n.Name)

2527
2017-11-18 13:41



Cảm ơn câu trả lời tất nhiên ... Nhưng thay vì Var movies = _db.Movies.Orderby(c => c.Category).ThenBy(n => n.Name)     nếu tôi sử dụng Var movies = _db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name)   2 lần "orderBy" tại sao kết quả lại khác?
@devendra, kết quả là khác nhau bởi vì thứ hai "OrderBy" hoạt động trên bộ sưu tập đó là kết quả của "OrderBy" đầu tiên và sắp xếp lại các mục của nó
Làm thế nào trên trái đất tôi đã đi tất cả thời gian này mà không biết về ThenBy?! (Chỉnh sửa: có vẻ như nó đã được giới thiệu trong .NET 4.0, giải thích cách nó trượt qua tôi không được chú ý.) - Jordan Gray
Điều này đã có từ LINQ đã được thêm vào. Câu trả lời này là trước .NET 4.0. - Nathan W
Có, tôi kết luận rằng quá vội vàng dựa trên 3,5 không nằm trong bản thả xuống trong phiên bản trang tài liệu; Tôi nên đã xem xét tất cả các con đường xuống cho các thông tin phiên bản. Cảm ơn vì sự đúng đắn của bạn. :) - Jordan Gray


Sử dụng LINQ không phải lambda, truy vấn cú pháp LINQ, bạn có thể làm điều này:

var movies = from row in _db.Movies 
             orderby row.Category, row.Name
             select row;

[EDIT để giải quyết địa chỉ] Để kiểm soát thứ tự sắp xếp, hãy sử dụng từ khóa ascending (đó là mặc định và do đó không đặc biệt hữu ích) hoặc descending, như vậy:

var movies = from row in _db.Movies 
             orderby row.Category descending, row.Name
             select row;

552
2017-09-30 14:49



Không có một cách để lật qua lại giữa giảm dần và không trong cú pháp này là có? - ehdv
Trên thực tế, câu trả lời của bạn tương đương với _db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name). Chính xác hơn là from row in _db.Movies orderby row.Category descending orderby row.Name select row - Lodewijk
@ Lodewijk: Tôi tin rằng bạn có chính xác ngược. Ví dụ của bạn sẽ kết thúc có hàng.Name là cột chính và hàng.Cục thứ cấp, tương đương với _db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name). Hai đoạn mã bạn cung cấp tương đương với nhau, chứ không phải của OP. - Scott Stafford
Nhược điểm duy nhất để sử dụng cú pháp SQL cho LINQ là không phải tất cả các chức năng đều được hỗ trợ, nhưng không phải tất cả - b729sefc


Thêm mới":

var movies = _db.Movies.OrderBy( m => new { m.CategoryID, m.Name })

Điều đó hoạt động trên hộp của tôi. Nó trả lại một cái gì đó có thể được sử dụng để sắp xếp. Nó trả về một đối tượng có hai giá trị.

Tương tự, nhưng khác nhau để sắp xếp theo một cột kết hợp, như sau.

var movies = _db.Movies.OrderBy( m => (m.CategoryID.ToString() + m.Name))

67
2018-06-22 14:27



Hãy cẩn thận khi sử dụng nó cho số. - WoF_Angel
Bạn có thể sử dụng OrderByDescending và ThenBy hoặc OrderBy và ThenByDescending, tùy theo nhu cầu của bạn. - Ali Shah Ahmed
Tôi khá chắc chắn về điều đó .OrderBy( m => new { m.CategoryID, m.Name }) và .OrderBy( m => new { m.Name, m.CategoryID }) sẽ tạo ra kết quả tương tự thay vì tôn trọng mức độ ưu tiên dự định. Nó đôi khi sẽ xuất hiện để cung cấp cho bạn thứ tự bạn muốn hoàn toàn bằng sự trùng hợp ngẫu nhiên. Ngoài ra m.CategoryID.ToString() + m.Name sẽ tạo ra thứ tự không chính xác nếu CategoryID là một int. Ví dụ: một cái gì đó có id = 123, name = 5times sẽ xuất hiện sau id = 1234, name = something thay vì trước đó. Nó cũng không phải là không hiệu quả để làm so sánh chuỗi, nơi so sánh int có thể xảy ra. - AaronLS
Khi tôi cố gắng sắp xếp theo kiểu ẩn danh, tôi nhận được một ArgumentException với thông báo "Ít nhất một đối tượng phải triển khai IComparable." Tôi thấy những người khác phải tuyên bố một so sánh khi làm điều này. Xem stackoverflow.com/questions/10356864/… . - Robert Gowland
Điều này hoàn toàn sai. Sắp xếp theo loại ẩn danh mới không có triển khai ICompariable không thể hoạt động, bởi vì không có thứ tự các thuộc tính của một loại ẩn danh. Nó sẽ không biết liệu để sắp xếp trên CategoryID đầu tiên hoặc Tên đầu tiên, hãy để một mình nếu họ đã được sắp xếp theo thứ tự ngược lại. - Triynko


sử dụng dòng sau trên DataContext của bạn để đăng nhập hoạt động SQL trên DataContext vào bàn điều khiển - sau đó bạn có thể thấy chính xác những gì các câu lệnh linq của bạn đang yêu cầu từ cơ sở dữ liệu:

_db.Log = Console.Out

Các câu lệnh LINQ sau:

var movies = from row in _db.Movies 
             orderby row.CategoryID, row.Name
             select row;

var movies = _db.Movies.OrderBy(m => m.CategoryID).ThenBy(m => m.Name);

tạo ra câu lệnh SQL sau:

SELECT [t0].ID, [t0].[Name], [t0].CategoryID
FROM [dbo].[Movies] as [t0]
ORDER BY [t0].CategoryID, [t0].[Name]

Trong khi đó, lặp lại một OrderBy trong LINQ, xuất hiện để đảo ngược kết quả đầu ra SQL:

var movies = from row in _db.Movies 
             orderby row.CategoryID
             orderby row.Name
             select row;

var movies = _db.Movies.OrderBy(m => m.CategoryID).OrderBy(m => m.Name);

tạo ra các SQL sau đây (Tên và CategoryId được chuyển đổi):

SELECT [t0].ID, [t0].[Name], [t0].CategoryID
FROM [dbo].[Movies] as [t0]
ORDER BY [t0].[Name], [t0].CategoryID

18
2018-02-18 21:03



Cảm ơn @Oliver đây là giải thích chi tiết về cách quan sát sự khác biệt và thực hiện cuộc gọi chính xác cho tình huống của bạn - Chris Schaller


Có ít nhất một cách để làm điều này bằng cách sử dụng LINQ, mặc dù không phải là dễ nhất. Bạn có thể làm điều đó bằng cách sử dụng OrberBy() phương pháp sử dụng IComparer. Trước tiên, bạn cần thực hiện một IComparer cho Movie lớp như thế này:

public class MovieComparer : IComparer<Movie>
{
    public int Compare(Movie x, Movie y)
    {
        if (x.CategoryId == y.CategoryId)
        {
            return x.Name.CompareTo(y.Name);
        }
        else
        {
            return x.CategoryId.CompareTo(y.CategoryId);
        }
    }
}

Sau đó, bạn có thể đặt phim với cú pháp sau:

var movies = _db.Movies.OrderBy(item => item, new MovieComparer());

Nếu bạn cần chuyển đổi thứ tự để giảm dần cho một trong các mục, chỉ cần chuyển x và y bên trong Compare()  phương pháp của MovieComparer cho phù hợp.


12
2018-04-03 14:07



Tôi thích điều này như là tổng quát hơn so với sau đó bởi vì bạn có thể làm những điều kỳ lạ với so sánh bao gồm có đối tượng khác nhau so sánh với các thuật toán khác nhau đã sẵn sàng để đi. Điều này là tốt hơn so với giải pháp ưa thích của tôi trước khi tìm hiểu về thenby đó là để tạo ra một lớp học mà thực hiện các giao diện IComparable. - Gerard ONeill
Kể từ năm 2012 (phiên bản .NET 4.5) bạn không phải tạo một lớp MovieComparer bản thân bạn; thay vào đó bạn có thể làm _db.Movies.OrderBy(item => item, Comparer<Movie>.Create((x, y) => { if (x.CategoryId == y.CategoryId) { return x.Name.CompareTo(y.Name); } else { return x.CategoryId.CompareTo(y.CategoryId); } }));. Tất nhiên, nếu bạn thích viết logic như một biểu thức, thay vì if...else, sau đó là lamda (x, y) => exprcó thể đơn giản hơn. - Jeppe Stig Nielsen


Tôi đã tạo ra một số phương pháp mở rộng (dưới đây), do đó bạn không phải lo lắng nếu một IQueryable đã được đặt hàng hay không. Nếu bạn muốn sắp xếp theo nhiều thuộc tính, hãy thực hiện như sau:

// We do not have to care if the queryable is already sorted or not. 
// The order of the Smart* calls defines the order priority
queryable.SmartOrderBy(i => i.Property1).SmartOrderByDescending(i => i.Property2);

Điều này đặc biệt hữu ích nếu bạn tạo thứ tự động, f.e. từ danh sách thuộc tính để sắp xếp.

public static class IQueryableExtension
{
    public static bool IsOrdered<T>(this IQueryable<T> queryable) {
        if(queryable == null) {
            throw new ArgumentNullException("queryable");
        }

        return queryable.Expression.Type == typeof(IOrderedQueryable<T>);
    }

    public static IQueryable<T> SmartOrderBy<T, TKey>(this IQueryable<T> queryable, Expression<Func<T, TKey>> keySelector) {
        if(queryable.IsOrdered()) {
            var orderedQuery = queryable as IOrderedQueryable<T>;
            return orderedQuery.ThenBy(keySelector);
        } else {
            return queryable.OrderBy(keySelector);
        }
    }

    public static IQueryable<T> SmartOrderByDescending<T, TKey>(this IQueryable<T> queryable, Expression<Func<T, TKey>> keySelector) {
        if(queryable.IsOrdered()) {
            var orderedQuery = queryable as IOrderedQueryable<T>;
            return orderedQuery.ThenByDescending(keySelector);
        } else {
            return queryable.OrderByDescending(keySelector);
        }
    }
}

12
2018-06-08 14:49



Câu trả lời này là vàng! Tôi sẽ kết hợp kiểm tra cho queryable.IsOrdered () với câu trả lời từ bài đăng này, để có một phương thức duy nhất có một hướng sắp xếp: stackoverflow.com/questions/388708 - SwissCoder
Bằng cách này Linq thực hiện nên đi ở nơi đầu tiên! OrderBy được thiết kế tồi ... - Andrzej Martyna


Nếu sử dụng kho lưu trữ chung

> lstModule = _ModuleRepository.GetAll().OrderBy(x => new { x.Level,
> x.Rank}).ToList();

khác

> _db.Module.Where(x=> ......).OrderBy(x => new { x.Level, x.Rank}).ToList();

0
2017-07-17 14:29