Đơn đặt hàng nhiều người khác bởi người nổi tiếng trong LINQ


1582

Tôi có hai bảng, moviescategories, tôi nhận được một danh sách theo thứ tự theo thể loạiID trước và sau đó theo Tên .

Bảng phim có ba cột ID, Tên và CategoryID . Bảng danh mục có hai 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 })

Đây là lý do tại sao điều này không thể hoạt động: Biểu thức lambda trong ngoặc đơn được cho là trả về một giá trị có thể được sử dụng để đặt hàng các mặt hàng: m.C CategoryID là một số có thể được sử dụng để đặt hàng các mặt hàng. Nhưng "m.C CategoryID, m.Name" không có ý nghĩa trong bối cảnh này.
chiccodoro

9
.ThenBy là những gì bạn đang tìm kiếm?
eka808

Nếu có bất kỳ cơ hội nào bạn muốn sắp xếp chúng theo thứ tự giảm dần thì đây là cách để đi.
RBT

Câu trả lời:


2831

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

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

4
Cảm ơn câu trả lời của khóa học ... 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" thì tại sao kết quả lại khác nhau?

147
@devendra, kết quả khác nhau vì "OrderBy" thứ hai 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ó

68
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ư được giới thiệu trong .NET 4.0, điều này giải thích cách nó lướt qua tôi mà không được chú ý.)
Jordan Gray

13
Điều này đã có từ khi LINQ được thêm vào. Câu trả lời này là .NET 4.0.
Nathan W

10
Có, tôi đã kết luận rằng quá vội vàng dựa trên 3.5 không có trong phiên bản thả xuống trong trang tài liệu ; Tôi nên đã tìm mọi cách để biết thông tin phiên bản. Cảm ơn vì sự đúng đắn của bạn. :)
Jordan Gray

594

Sử dụng LINQ không cú pháp, cú pháp truy vấn, 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 nhận xét] Để kiểm soát thứ tự sắp xếp, hãy sử dụng các 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;

1
Không có cách nào để lật qua lại giữa giảm dần và không theo cú pháp này?
ehv

1
Trên thực tế, câu trả lời của bạn là 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

9
@Lodewijk: Tôi tin rằng bạn có chính xác điều đó ngược. Ví dụ của bạn sẽ kết thúc có hàng. Được coi là cột chính và hàng. Thể loại phụ, 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, không phải của OP.
Scott Stafford

6
Nhược điểm duy nhất của việc sử dụng cú pháp SQL cho Linq là không phải tất cả các hàm đều được hỗ trợ, hầu hết nhưng không phải tất cả
Joshua G

74

Thêm mới":

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

Điều đó làm việc trên hộp của tôi. Nó trả về 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 với sắp xếp theo cột kết hợp, như sau.

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

21
Hãy cẩn thận khi sử dụng số đó.
WoF_Angel

7
Bạn có thể sử dụng OrderByDesceinating và ThenBy, hoặc OrderBy và ThenByDesceinating, tùy theo nhu cầu của bạn.
Ali Shah Ahmed

6
Tôi khá chắc chắn rằng .OrderBy( m => new { m.CategoryID, m.Name }).OrderBy( m => new { m.Name, m.CategoryID })sẽ tạo ra kết quả tương tự thay vì tôn trọng ưu tiên dự định. Đôi khi nó sẽ xuất hiện để cung cấp cho bạn thứ tự bạn muốn hoàn toàn bởi sự trùng hợp. Ngoài ra m.CategoryID.ToString() + m.Namesẽ 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 = cái gì đó thay vì trước đó. Nó cũng không hiệu quả để thực hiện so sánh chuỗi trong đó so sánh int có thể xảy ra.
AaronLS

7
Khi tôi cố gắng đặt hàng theo loại ẩ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 khai báo một so sánh khi làm điều này. Xem stackoverflow.com/questions/10356864/ cấp .
Robert Gowland

4
Điều này là hoàn toàn sai. Đặt hàng theo loại ẩn danh mới không có triển khai ICompariable có thể hoạt động, bởi vì không có thứ tự cho các thuộc tính của loại ẩn danh. Sẽ không biết nên sắp xếp trên CategoryID trước hay Tên trước, nếu không chúng sẽ được sắp xếp theo thứ tự ngược lại.
Triynko

29

sử dụng dòng sau trên DataContext của bạn để ghi nhật ký 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 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 SQL sau:

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

Trong khi đó, việc lặp lại một OrderBy trong Linq, dường như đả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 SQL sau (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

24

Tôi đã tạo một số phương thức tiện ích mở rộng (bên dưới) để bạn không phải lo lắng liệu IQueryable đã được đặt hàng hay chưa. Nếu bạn muốn đặt hàng theo nhiều thuộc tính, chỉ cần làm 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, từ một danh sách các thuộc tính cần 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);
        }
    }
}

1
Câu trả lời này là vàng! Tôi sẽ kết hợp việc kiểm tra cho queryable.IsOrdered () với câu trả lời từ bài này, để có một phương pháp duy nhất mở ra một hướng sắp xếp: stackoverflow.com/questions/388708
SwissCoder

1
Cách này thực hiện Linq nên đi ngay từ đầu! OrderBy được thiết kế tồi ...
Andrzej Martyna

1
@Marie bạn rõ ràng không hiểu trường hợp sử dụng. Làm thế nào để bạn tạo một thứ tự động từ một danh sách các thuộc tính chẳng hạn? Đây là cách duy nhất. Xin đừng downvote và xem xét lại. Cảm ơn bạn.
sjkm

Đủ công bằng. Tôi vẫn không thấy lợi ích nhưng tôi sẽ lấy lại phiếu bầu của mình
Marie

điều này sẽ làm việc vớinullable datetime
aggie

16

Có ít nhất một cách nữa để làm điều này bằng cách sử dụng LINQ, mặc dù không phải là cách dễ nhất. Bạn có thể làm điều đó bằng cách sử dụng OrberBy()phương thức sử dụng một IComparer. Trước tiên, bạn cần phải thực hiện một IComparercho Movielớ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 hàng các bộ phim với cú pháp sau:

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

Nếu bạn cần chuyển 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 thức MovieComparertương ứng.


1
Tôi thích điều này vì nó chung chung hơn so với lúc đó vì bạn có thể làm những điều kỳ lạ với việc so sánh bao gồm việc có các đối tượng so sánh khác nhau với các thuật toán khác nhau sẵn sàng để đi. Điều này tốt hơn giải pháp ưa thích của tôi trước khi tìm hiểu về việc tạo ra một lớp thực hiện giao diện IComparable.
Gerard ONeill

1
Kể từ năm 2012 (phiên bản .NET 4.5), bạn không phải tự tạo một lớp MovieComparer; 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 dưới dạng một biểu thức, thay vì if... else, thì lamda (x, y) => exprcó thể đơn giản hơn.
Jeppe Stig Nielsen

3

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

1
Các biểu thức ẩn danh sẽ được phân tích cú pháp cục bộ bởi lõi khung thực thể. Biểu thức LINQ không thể được dịch và sẽ được đánh giá cục bộ.
alhpe
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.