Banyak "pesan oleh" di LINQ

1582

Saya memiliki dua tabel, moviesdan categories, dan saya mendapatkan daftar yang diurutkan berdasarkan CategoryID pertama dan kemudian dengan Nama .

Tabel film memiliki tiga kolom ID, Nama dan CategoryID . Tabel kategori memiliki dua kolom ID dan Nama .

Saya mencoba sesuatu seperti yang berikut ini, tetapi tidak berhasil.

var movies = _db.Movies.OrderBy( m => { m.CategoryID, m.Name })
Sasha
sumber
Inilah mengapa ini tidak bisa berfungsi: Ekspresi lambda dalam tanda kurung seharusnya mengembalikan nilai yang dapat digunakan untuk memesan item: m.CategoryID adalah angka yang dapat digunakan untuk memesan item. Tetapi "m.CategoryID, m.Name" tidak masuk akal dalam konteks ini.
chiccodoro
9
.Lalu apa yang Anda cari?
eka808
Jika kebetulan Anda ingin menyortirnya dalam urutan yang menurun, berikut adalah caranya.
RBT

Jawaban:

2831

Ini seharusnya bekerja untuk Anda:

var movies = _db.Movies.OrderBy(c => c.Category).ThenBy(n => n.Name)
Nathan W
sumber
4
Terima kasih atas jawabannya tentu saja ... Tetapi alih-alih Var movies = _db.Movies.Orderby(c => c.Category).ThenBy(n => n.Name) jika saya menggunakan Var movies = _db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name) 2 kali "orderBy" mengapa hasilnya berbeda?
147
@devendra, hasilnya berbeda karena "OrderBy" yang kedua bekerja di atas koleksi yang merupakan hasil dari "OrderBy" dan menyusun ulang itemnya
68
Bagaimana saya bisa pergi selama ini tanpa mengetahui ThenBy ?! ( Sunting: sepertinya diperkenalkan di .NET 4.0, yang menjelaskan bagaimana itu melewati saya tanpa disadari.)
Jordan Gray
13
Ini sudah ada sejak LINQ ditambahkan. Jawaban ini adalah pra .NET 4.0.
Nathan W
10
Ya, saya menyimpulkan bahwa terlalu cepat berdasarkan pada 3,5 yang tidak ada dalam dropdown versi di halaman dokumentasi ; Saya seharusnya melihat jauh ke bawah untuk informasi versi. Terima kasih atas koreksinya. :)
Jordan Gray
594

Menggunakan LINQ non-lambda, query-sintaks, Anda dapat melakukan ini:

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

[EDIT ke alamat komentar] Untuk mengontrol urutan, gunakan kata kunci ascending(yang merupakan default dan karenanya tidak terlalu berguna) atau descending, seperti:

var movies = from row in _db.Movies 
             orderby row.Category descending, row.Name
             select row;
Scott Stafford
sumber
1
Tidak ada cara untuk bolak-balik antara turun dan tidak dalam sintaks ini, kan?
ehdv
1
Sebenarnya, jawaban Anda setara dengan _db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name). Lebih tepatnya adalahfrom row in _db.Movies orderby row.Category descending orderby row.Name select row
Lodewijk
9
@Lodewijk: Saya yakin Anda memiliki itu persis mundur. Contoh Anda akan berakhir dengan baris. Nama menjadi kolom utama dan baris. Kategori sekunder, yang setara dengan _db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name). Dua cuplikan yang Anda berikan setara satu sama lain, bukan untuk OP.
Scott Stafford
6
Satu-satunya downside menggunakan sintaks SQL untuk Linq adalah bahwa tidak semua fungsi didukung, sebagian besar tetapi tidak semua
Joshua G
74

Tambah baru":

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

Itu bekerja di kotak saya. Itu mengembalikan sesuatu yang dapat digunakan untuk mengurutkan. Ini mengembalikan objek dengan dua nilai.

Serupa, tetapi berbeda dengan penyortiran menurut kolom gabungan, seperti berikut.

var movies = _db.Movies.OrderBy( m => (m.CategoryID.ToString() + m.Name))
Alex
sumber
21
Hati-hati saat menggunakannya untuk angka.
WoF_Angel
7
Anda dapat menggunakan OrderByDescending dan ThenBy, atau OrderBy dan ThenByDescending, tergantung kebutuhan Anda.
Ali Shah Ahmed
6
Saya cukup yakin itu .OrderBy( m => new { m.CategoryID, m.Name })dan .OrderBy( m => new { m.Name, m.CategoryID })akan menghasilkan hasil yang sama daripada menghormati prioritas yang dimaksud. Kadang-kadang akan muncul memberi Anda pemesanan yang Anda inginkan murni secara kebetulan. Selain itu m.CategoryID.ToString() + m.Nameakan menghasilkan pemesanan yang salah jika CategoryID adalah int. Misalnya, sesuatu dengan id = 123, nama = 5 kali akan muncul setelah id = 1234, nama = sesuatu, bukan sebelumnya. Ini juga tidak efisien untuk melakukan perbandingan string di mana perbandingan int dapat terjadi.
AaronLS
7
Ketika saya mencoba memesan dengan jenis anonim, saya mendapatkan ArgumentException dengan pesan "Setidaknya satu objek harus mengimplementasikan IComparable.". Saya melihat orang lain harus menyatakan pembanding ketika melakukan ini. Lihat stackoverflow.com/questions/10356864/… .
Robert Gowland
4
Ini benar-benar salah. Memesan dengan jenis anonim baru yang tidak memiliki implementasi ICompariable tidak dapat berfungsi, karena tidak ada pesanan ke properti jenis anonim. Ia tidak akan tahu apakah akan mengurutkan pada CategoryID terlebih dahulu atau Nama terlebih dahulu, apalagi jika mereka akan disortir dalam urutan yang berlawanan.
Triynko
29

gunakan baris berikut pada DataContext Anda untuk mencatat aktivitas SQL pada DataContext ke konsol - maka Anda dapat melihat dengan tepat apa yang diminta oleh pernyataan LINQ Anda dari database:

_db.Log = Console.Out

Pernyataan LINQ berikut:

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

DAN

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

menghasilkan SQL berikut:

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

Sedangkan, mengulang OrderBy di Linq, tampaknya membalikkan hasil SQL yang dihasilkan:

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

DAN

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

menghasilkan SQL berikut (Nama dan CategoryId diaktifkan):

SELECT [t0].ID, [t0].[Name], [t0].CategoryID
FROM [dbo].[Movies] as [t0]
ORDER BY [t0].[Name], [t0].CategoryID
Oliver Slay
sumber
24

Saya telah membuat beberapa metode ekstensi (di bawah) sehingga Anda tidak perlu khawatir jika IQueryable sudah dipesan atau belum. Jika Anda ingin memesan oleh beberapa properti cukup lakukan sebagai berikut:

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

Ini sangat membantu jika Anda membuat urutan secara dinamis, dari daftar properti yang akan disortir.

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);
        }
    }
}
sjkm
sumber
1
Jawaban ini adalah emas! Saya akan menggabungkan cek untuk queryable.IsOrdered () dengan jawaban dari posting ini, untuk memiliki metode tunggal yang mengambil arah pengurutan: stackoverflow.com/questions/388708
SwissCoder
1
Dengan cara ini implementasi Linq harus di tempat pertama! OrderBy dirancang dengan buruk ...
Andrzej Martyna
1
@Marie Anda jelas belum mengerti kasus penggunaannya. Bagaimana Anda membuat pemesanan secara dinamis dari daftar properti misalnya? Ini satu-satunya jalan. Tolong jangan downvote dan pertimbangkan kembali. Terima kasih.
sjkm
Cukup adil. Saya masih tidak melihat manfaatnya tetapi saya akan mengambil suara kembali
Marie
akankah ini bekerja dengannullable datetime
aggie
16

Setidaknya ada satu cara lagi untuk melakukan ini menggunakan LINQ, meskipun bukan yang termudah. Anda bisa melakukannya dengan menggunakan OrberBy()metode yang menggunakan IComparer. Pertama, Anda perlu menerapkan ICompareruntuk Moviekelas seperti ini:

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

Kemudian Anda dapat memesan film dengan sintaks berikut:

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

Jika Anda perlu mengganti urutan ke salah satu item, cukup ganti x dan y di dalam Compare() metode yang MovieComparersesuai.

prudentcoder
sumber
1
Saya suka ini lebih umum daripada saat itu karena Anda dapat melakukan hal-hal aneh dengan membandingkan termasuk memiliki objek membandingkan berbeda dengan algoritma yang berbeda siap untuk pergi. Ini lebih baik daripada solusi pilihan saya sebelum belajar tentang thenby yang menciptakan kelas yang mengimplementasikan antarmuka IComparable.
Gerard ONeill
1
Sejak 2012 (.NET versi 4.5) Anda tidak perlu membuat kelas MovieComparersendiri; Anda bisa melakukannya _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); } }));. Tentu saja, jika Anda lebih suka menulis logika sebagai satu ekspresi, daripada if... else, maka lamda (x, y) => exprbisa lebih sederhana.
Jeppe Stig Nielsen
3

Jika menggunakan repositori generik

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

lain

> _db.Module.Where(x=> ......).OrderBy(x => new { x.Level, x.Rank}).ToList();
Saint-martin Guillaume
sumber
1
Ekspresi anonim akan diuraikan secara lokal oleh inti kerangka kerja entitas. Ekspresi LINQ tidak dapat diterjemahkan dan akan dievaluasi secara lokal.
alhpe