LINQ'da birden fazla "sıralama ölçütü"


1582

İki tablo var, moviesve categories, ve ben tarafından sıralı bir listesini almak KategoriNo tarafından birinci ve daha sonra adı .

Film tablosunda üç sütun Kimliği, Ad ve Kategori Kimliği vardır . Kategori tablosunun iki sütun kimliği ve adı vardır .

Aşağıdaki gibi bir şey denedim, ama işe yaramadı.

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

İşte bu neden işe yaramaz: Parantez içindeki lambda ifadesinin, öğeleri sipariş etmek için kullanılabilecek bir değer döndürmesi gerekir: m.CategoryID, öğeleri sipariş etmek için kullanılabilecek bir sayıdır. Ancak "m.CategoryID, m.Name" bu bağlamda bir anlam ifade etmiyor.
chiccodoro

9
Aradığın şey ne?
eka808

Herhangi bir şans eseri onları azalan düzende sıralamak istiyorsanız, işte burada bir yol var.
RBT

Yanıtlar:


2831

Bunun sizin için çalışması gerekir:

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

4
Elbette cevap için teşekkürler ... Ama 2 kez "orderBy" Var movies = _db.Movies.Orderby(c => c.Category).ThenBy(n => n.Name) kullanmak yerine Var movies = _db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name)sonuç neden farklı?

147
@devendra, sonuç farklı çünkü ikinci "OrderBy" ilk "OrderBy" nin sonucu olan koleksiyon üzerinde çalışıyor ve öğelerini yeniden

68
Bunca zaman bilmeden nasıl yeryüzüne gittim ThenBy?! ( Düzenle: Görünüşe göre beni fark etmeden nasıl geçtiğini açıklayan .NET 4.0'da tanıtıldı.)
Jordan Gray

13
Bu LINQ eklendiğinden beri var. Bu yanıt .NET 4.0 öncesi.
Nathan W

10
Evet, çok aceleyle 3.5'in belge sayfasındaki sürüm açılır listesinde olmadığına karar verdim ; Sürüm bilgisi için aşağı bakmalıydım. Düzeltme için teşekkürler. :)
Jordan Gray

594

Lambda olmayan, sorgu-sözdizimi LINQ kullanarak şunları yapabilirsiniz:

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

[Yorumu adreslemek için DÜZENLE] Sıralama düzenini kontrol etmek için ascending(varsayılan olan ve bu nedenle özellikle kullanışlı olmayan) anahtar kelimeleri kullanın veya descendingşu şekilde:

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

1
Bu sözdiziminde azalan ve olmayan arasında gidip gelmenin bir yolu yok mu?
ehdv

1
Aslında cevabınız buna eşdeğerdir _db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name). Daha doğrufrom row in _db.Movies orderby row.Category descending orderby row.Name select row
Lodewijk

9
@Lodewijk: Tam olarak geriye doğru olduğuna inanıyorum. Örneğiniz, row.Name öğesinin birincil sütun ve row.Category sekonder olan eşdeğeri olacak _db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name). Sağladığınız iki snippet OP'lere değil, birbirine eşdeğerdir.
Scott Stafford

6
Linq için SQL sözdizimini kullanmanın tek dezavantajı, işlevlerin hepsinin değil, hepsinin değil desteklenmesidir
Joshua G

74

Yeni ekle":

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

Bu benim kutumda çalışıyor. Sıralamak için kullanılabilecek bir şey döndürür. İki değerli bir nesne döndürür.

Benzer, ancak aşağıdaki gibi birleştirilmiş bir sütuna göre sıralamadan farklıdır.

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

21
Sayılar için kullanırken dikkatli olun.
WoF_Angel

7
İhtiyacınıza bağlı olarak OrderByDescending ve ThenBy veya OrderBy ve ThenByDescending kullanabilirsiniz.
Ali Şah Ahmed

6
Oldukça emin değilim .OrderBy( m => new { m.CategoryID, m.Name })ve .OrderBy( m => new { m.Name, m.CategoryID })amaçlanan öncelik saygı yerine aynı sonuçları üretecektir. Bazen size sadece tesadüfle istediğiniz düzeni veriyor gibi görünecektir. m.CategoryID.ToString() + m.NameCategoryID bir ise ek olarak yanlış siparişleri üretecektir int. Örneğin, id = 123, name = 5times olan bir şey id = 1234'ten sonra görünecektir, name = daha önce değil. Ayrıca int karşılaştırmalarının olabileceği dize karşılaştırmalarını yapmak da yetersizdir.
AaronLS

7
Ben anonim bir tür tarafından sipariş çalıştığınızda, "En az bir nesne IComparable uygulamak gerekir" iletisiyle bir ArgumentException alıyorum. Bunu yaparken başkalarının bir karşılaştırıcı beyan etmesi gerektiğini görüyorum. Bkz. Stackoverflow.com/questions/10356864/… .
Robert Gowland

4
Bu kesinlikle yanlış. ICompariable uygulaması olmayan yeni bir anonim türle sipariş vermek, anonim türün özelliklerine yönelik bir sipariş olmadığından, çalışamaz. Ters sıralarda sıralanırlarsa, önce CategoryID veya First Name üzerinde sıralama yapıp yapamayacağını bilemez.
Triynko

29

DataContext üzerindeki SQL etkinliğini konsola kaydetmek için DataContext'inizde aşağıdaki satırı kullanın - o zaman linq deyimlerinizin veritabanından ne istediğini tam olarak görebilirsiniz:

_db.Log = Console.Out

Aşağıdaki LINQ ifadeleri:

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

VE

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

aşağıdaki SQL'i üretin:

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

Linq içinde bir OrderBy tekrarlamak sonuçta ortaya çıkan SQL çıktısını tersine çevirir gibi görünüyor:

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

VE

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

aşağıdaki SQL'i üretin (Name ve CategoryId anahtarlamalı):

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

24

Bazı uzantı yöntemleri (aşağıda) oluşturdum, bu yüzden bir IQueryable zaten sipariş edilip edilmediğini düşünmenize gerek yok. Birden fazla mülke göre sipariş vermek istiyorsanız, aşağıdaki gibi yapın:

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

Bu, sıralamayı dinamik olarak, fe listelemek için özellikler listesinden oluşturursanız özellikle yararlıdır.

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
Bu cevap altın! Sıralama yönünü alan tek bir yönteme sahip olmak için queryable.IsOrdered () işaretini bu gönderinin yanıtıyla birleştireceğim: stackoverflow.com/questions/388708
SwissCoder

1
Bu şekilde Linq uygulaması ilk etapta yapılmalıdır!
Andrzej Martyna

1
@ Dostum, kullanım durumunu açıkça anlamadınız. Örneğin bir özellik listesinden dinamik olarak nasıl sipariş verirsiniz? Bu tek yoldur. Lütfen küçümsemeyin ve yeniden düşünmeyin. Teşekkür ederim.
sjkm

Yeterince adil. Hala fayda görmüyorum ama oyumu geri alacağım
Marie

bu ile çalışacaknullable datetime
aggie

16

En kolay olmasa da, LINQ kullanarak bunu yapmanın en az bir yolu daha var. Bunu OrberBy()bir IComparer. Yöntemini kullanarak yapabilirsiniz . İlk olarak, bir uygulamaya gerek ICompareriçin Movieböyle sınıfa:

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

Ardından filmleri aşağıdaki sözdizimiyle sipariş edebilirsiniz:

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

Eğer öğelerinden biri için inen için sipariş geçmek gerekirse sadece iç x ve y geçiş Compare() yöntemiMovieComparer buna göre.


1
Bundan sonra farklı algoritmalarla farklı karşılaştırma nesnelerine sahip olmak da dahil olmak üzere karşılaştırma ile garip şeyler yapabileceğiniz için bunu daha genel olarak seviyorum. Bu, IComparable arabirimini uygulayan bir sınıf yaratmak olan o zamandan beri öğrenmeden önce tercih ettiğim çözümden daha iyidir.
Gerard ONeill

1
2012'den beri (.NET sürüm 4.5) MovieComparerkendiniz bir sınıf oluşturmanız gerekmez; bunun yerine yapabilirsiniz _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); } }));. Elbette, mantığı if... yerine tek bir ifade olarak yazmayı tercih ederseniz else, lamda (x, y) => exprdaha basit olabilir.
Jeppe Stig Nielsen

3

Genel depo kullanılıyorsa

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

Başka

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

1
Anonim ifadeler varlık çerçevesi çekirdeği tarafından yerel olarak ayrıştırılacaktır. LINQ ifadesi çevrilemedi ve yerel olarak değerlendirilecek.
alhpe
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.