หลาย“ สั่งซื้อโดย” ใน LINQ


1582

ฉันมีสองตารางmoviesและcategoriesและฉันได้รับรายการสั่งซื้อโดยCategoryIDแรกแล้วตามด้วยชื่อ

ตารางหนังเรื่องนี้มีสามคอลัมน์ID, ชื่อและที่ CategoryID ตารางหมวดหมู่ที่มีสองคอลัมน์ID และชื่อ

ฉันลองทำสิ่งต่อไปนี้ แต่ไม่ได้ผล

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

นี่คือสาเหตุที่ไม่สามารถใช้งานได้: นิพจน์แลมบ์ดาในวงเล็บควรส่งคืนค่าที่สามารถใช้ในการสั่งซื้อรายการ: m.CategoryID เป็นหมายเลขที่สามารถใช้ในการสั่งซื้อรายการ แต่ "m.CategoryID, m.Name" ไม่สมเหตุสมผลในบริบทนี้
chiccodoro

9
แล้วสิ่งที่คุณกำลังค้นหาคืออะไร?
eka808

หากมีโอกาสที่คุณต้องการจัดเรียงตามลำดับจากมากไปน้อยที่นี่เป็นวิธีที่จะไป
RBT

คำตอบ:


2831

สิ่งนี้น่าจะเหมาะกับคุณ:

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

4
ขอบคุณสำหรับคำตอบที่แน่นอน ... แต่Var movies = _db.Movies.Orderby(c => c.Category).ThenBy(n => n.Name) ถ้าฉันใช้ Var movies = _db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name) "orderBy" แทน 2 ครั้งเหตุใดผลลัพธ์จึงแตกต่างกัน

147
@devendra ผลลัพธ์แตกต่างกันเพราะ "OrderBy" ลำดับที่สองทำงานผ่านคอลเลกชันซึ่งเป็นผลมาจาก "OrderBy" ตัวแรกและเรียงลำดับรายการใหม่

68
ฉันไปบนโลกนี้ได้ตลอดเวลาโดยไม่รู้ตัวThenBy! ( แก้ไข:ดูเหมือนว่ามันถูกนำมาใช้ใน. NET 4.0 ซึ่งอธิบายถึงวิธีที่มันผ่านมาโดยที่ฉันไม่ได้สังเกต)
Jordan Gray Gray

13
สิ่งนี้มีอยู่นับตั้งแต่มีการเพิ่ม LINQ คำตอบนี้คือ pre .NET 4.0
นาธาน W

10
ใช่ฉันได้ข้อสรุปว่าตามเร่งรีบเกินไปใน 3.5 ไม่อยู่ในแบบเลื่อนรุ่นในหน้าเอกสาร ; ฉันควรดูข้อมูลเวอร์ชั่นทั้งหมด ขอบคุณสำหรับการแก้ไข :)
Jordan Gray

594

ใช้ non-lambda, query-syntax LINQ คุณสามารถทำได้:

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

[แก้ไขความคิดเห็นที่อยู่] เพื่อควบคุมลำดับการจัดเรียงให้ใช้คำหลักascending(ซึ่งเป็นค่าเริ่มต้นและดังนั้นจึงไม่มีประโยชน์โดยเฉพาะ) หรือdescendingเช่น:

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

1
ไม่มีวิธีพลิกกลับไปกลับมาระหว่างจากมากไปน้อยและไม่ใช่ในไวยากรณ์นี้หรือไม่
ehdv

1
_db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name)ที่จริงแล้วคำตอบของคุณคือเทียบเท่ากับ ถูกต้องมากขึ้นคือfrom row in _db.Movies orderby row.Category descending orderby row.Name select row
Lodewijk

9
@Lodewijk: ฉันเชื่อว่าคุณมีสิ่งที่ย้อนหลัง ตัวอย่างเช่นคุณจะสิ้นสุดมี row.Name เป็นคอลัมน์หลักและ row.Category _db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name)รองซึ่งเป็นเทียบเท่าของ ตัวอย่างสองรายการที่คุณระบุมีค่าเทียบเท่ากันไม่ใช่ของ OP
Scott Stafford

6
ข้อเสียเพียงอย่างเดียวของการใช้ไวยากรณ์ SQL สำหรับ Linq คือไม่รองรับฟังก์ชั่นทั้งหมดส่วนใหญ่ แต่ไม่ใช่ทั้งหมด
Joshua G

74

เพิ่มใหม่":

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

ใช้งานได้กับกล่องของฉัน มันจะคืนสิ่งที่สามารถใช้ในการจัดเรียง ส่งคืนวัตถุที่มีค่าสองค่า

คล้ายกัน แต่แตกต่างกันในการจัดเรียงตามคอลัมน์รวมดังนี้

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

21
ระวังเมื่อใช้สำหรับตัวเลข
WoF_Angel

7
คุณสามารถใช้ OrderByDescending และ ThenBy หรือ OrderBy และ ThenByDescending ขึ้นอยู่กับความต้องการของคุณ
Ali Shah Ahmed

6
ฉันค่อนข้างแน่ใจว่า.OrderBy( m => new { m.CategoryID, m.Name })และ.OrderBy( m => new { m.Name, m.CategoryID })จะให้ผลลัพธ์เดียวกันแทนที่จะเคารพลำดับความสำคัญที่ตั้งใจไว้ บางครั้งมันจะปรากฏขึ้นเพื่อให้คุณสั่งซื้อที่คุณต้องการอย่างหมดจดโดยบังเอิญ นอกจากm.CategoryID.ToString() + m.Nameจะผลิต orderings ไม่ถูกต้องถ้า CategoryID intเป็น ตัวอย่างเช่นบางสิ่งที่มี id = 123, name = 5times จะปรากฏขึ้นหลังจาก id = 1234, name = บางสิ่งบางอย่างแทนที่จะเป็นมาก่อน นอกจากนี้ยังไม่มีประสิทธิภาพในการทำการเปรียบเทียบสตริงที่การเปรียบเทียบ int อาจเกิดขึ้นได้
AaronLS

7
เมื่อฉันพยายามสั่งซื้อโดยใช้ประเภทที่ไม่ระบุชื่อฉันได้รับ ArgumentException พร้อมข้อความ "มีวัตถุอย่างน้อยหนึ่งรายการที่ต้องใช้ IComparable" ฉันเห็นคนอื่นต้องประกาศตัวเปรียบเทียบเมื่อทำสิ่งนี้ ดูstackoverflow.com/questions/10356864/...
Robert Gowland

4
นี่เป็นสิ่งที่ผิดอย่างยิ่ง การสั่งซื้อโดยไม่ระบุชื่อชนิดใหม่ที่ไม่มีการใช้งาน ICompariable ไม่สามารถใช้งานได้เนื่องจากไม่มีการสั่งซื้อคุณสมบัติของประเภทที่ไม่ระบุตัวตน มันจะไม่ทราบว่าจะเรียงลำดับใน CategoryID ก่อนหรือชื่อแรกให้นับประสาหากพวกเขาจะเรียงในคำสั่งตรงข้าม
Triynko

29

ใช้บรรทัดต่อไปนี้บน DataContext ของคุณเพื่อบันทึกกิจกรรม SQL บน DataContext ไปยังคอนโซล - จากนั้นคุณสามารถเห็นสิ่งที่คำสั่ง linq ของคุณร้องขอจากฐานข้อมูล:

_db.Log = Console.Out

คำสั่ง LINQ ต่อไปนี้:

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

สร้าง SQL ต่อไปนี้:

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

ในขณะที่ทำซ้ำ OrderBy ใน Linq ดูเหมือนจะย้อนกลับผลลัพธ์ 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);

สร้าง SQL ต่อไปนี้ (เปลี่ยนชื่อและ CategoryId):

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

24

ฉันได้สร้างวิธีการขยาย (ด้านล่าง) เพื่อให้คุณไม่ต้องกังวลว่า IQueryable นั้นได้รับคำสั่งแล้วหรือไม่ หากคุณต้องการที่จะสั่งซื้อโดยคุณสมบัติหลาย ๆ เพียงทำมันดังต่อไปนี้:

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

สิ่งนี้มีประโยชน์อย่างยิ่งหากคุณสร้างการสั่งซื้อแบบไดนามิก fe จากรายการคุณสมบัติที่จะเรียงลำดับ

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
คำตอบนี้คือทอง! ฉันจะรวมการตรวจสอบสำหรับ queryableIsOrdered () กับคำตอบจากโพสต์นี้เพื่อให้มีวิธีการเดียวที่ใช้ทิศทางการเรียงลำดับ: stackoverflow.com/questions/388708
SwissCoder

1
วิธีนี้การนำ Linq ไปใช้ในครั้งแรก! OrderBy ได้รับการออกแบบมาไม่ดี ...
Andrzej Martyna

1
@Marie คุณชัดเจนไม่เข้าใจกรณีการใช้งาน คุณจะสร้างการสั่งซื้อแบบไดนามิกจากรายการของคุณสมบัติได้อย่างไร นี่เป็นวิธีเดียวเท่านั้น กรุณาอย่าลงคะแนนและพิจารณาใหม่ ขอบคุณ.
sjkm

ยุติธรรมพอสมควร ฉันยังไม่เห็นประโยชน์ แต่จะนำคะแนนเสียงของฉันกลับมา
Marie

จะทำงานร่วมกับnullable datetime
aggie

16

มีอย่างน้อยหนึ่งวิธีในการทำเช่นนี้โดยใช้ LINQ แม้ว่าจะไม่ใช่วิธีที่ง่ายที่สุด คุณสามารถทำมันโดยใช้วิธีการที่ใช้OrberBy() IComparerก่อนอื่นคุณต้องใช้คลาสIComparerสำหรับสิ่งMovieนี้:

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

จากนั้นคุณสามารถสั่งซื้อภาพยนตร์ด้วยไวยากรณ์ต่อไปนี้:

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

หากคุณต้องการสลับการเรียงลำดับเป็นมากไปน้อยสำหรับหนึ่งในรายการเพียงแค่สลับ x และ y ในCompare() วิธีการMovieComparerตามลำดับ


1
ฉันชอบสิ่งนี้ว่าเป็นเรื่องทั่วไปมากกว่าตอนนั้นเนื่องจากคุณสามารถทำสิ่งแปลก ๆ ด้วยการเปรียบเทียบรวมถึงการมีวัตถุเปรียบเทียบที่แตกต่างกันพร้อมอัลกอริทึมที่แตกต่างกันพร้อมที่จะไป นี่เป็นวิธีที่ดีกว่าโซลูชันที่ฉันต้องการก่อนที่จะเรียนรู้เกี่ยวกับตอนนั้นซึ่งเป็นการสร้างคลาสที่ใช้อินเทอร์เฟซ IComparable
เจอราร์ดโอนีล

1
ตั้งแต่ปี 2012 (. NET เวอร์ชั่น 4.5) คุณไม่จำเป็นต้องสร้างคลาสMovieComparerด้วยตัวเอง _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); } }));แทนที่คุณสามารถทำได้ แน่นอนถ้าคุณต้องการเขียนตรรกะเป็นหนึ่งนิพจน์แทนที่จะเป็นif... elseแลมบ์ดา(x, y) => exprนั้นง่ายกว่า
Jeppe Stig Nielsen

3

ถ้าใช้ที่เก็บทั่วไป

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

อื่น

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

1
นิพจน์ที่ไม่ระบุชื่อจะถูกแยกวิเคราะห์ในเครื่องโดยแกนหลักเอนทิตี ไม่สามารถแปลนิพจน์ LINQ และจะได้รับการประเมินในพื้นที่
alhpe
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.