การสั่งซื้อ LINQ ตามคอลัมน์ null ซึ่งคำสั่งนั้นขึ้นและควรเป็นค่าสุดท้าย


141

ฉันพยายามเรียงลำดับรายการสินค้าตามราคา

LowestPriceชุดผลความต้องการที่จะรายการสินค้าตามราคาจากต่ำไปสูงจากคอลัมน์ อย่างไรก็ตามคอลัมน์นี้ไม่สามารถใช้ได้

ฉันสามารถเรียงลำดับรายการจากมากไปน้อยเช่น:

var products = from p in _context.Products
   where p.ProductTypeId == 1
   orderby p.LowestPrice.HasValue descending
   orderby p.LowestPrice descending
   select p;

// returns:    102, 101, 100, null, null

อย่างไรก็ตามฉันไม่สามารถหาวิธีเรียงลำดับจากน้อยไปหามาก

// i'd like: 100, 101, 102, null, null

11
orderby p.LowestPrice ?? Int.MaxValue;เป็นวิธีที่ง่าย
PostMan

3
@ โพสต์แมน: ใช่มันง่ายมันได้ผลลัพธ์ที่ถูกต้อง แต่OrderByDescending, ThenByชัดเจนกว่า
jason

@ สันใช่ผมไม่ทราบว่าไวยากรณ์orderbyและได้ติดตามด้านมองหามัน :)
PostMan

คำตอบ:


161

ลองวางทั้งสองคอลัมน์ในลำดับเดียวกันโดย

orderby p.LowestPrice.HasValue descending, p.LowestPrice

มิฉะนั้นแต่ละคำสั่งโดยจะเป็นการดำเนินการแยกกันในการรวบรวมลำดับใหม่ในแต่ละครั้ง

สิ่งนี้ควรเรียงลำดับด้วยค่าก่อน "ตามด้วย" ลำดับของค่า


22
ข้อผิดพลาดทั่วไปผู้คนทำเช่นเดียวกันกับ Lamda Syntax - การใช้. OrderBy สองครั้งแทนที่จะเป็น. ThenBy
DaveShaw

1
สิ่งนี้ใช้เพื่อสั่งซื้อฟิลด์ด้วยค่าที่อยู่ด้านบนและฟิลด์ว่างด้านล่างฉันใช้สิ่งนี้: orderby p.LowestPrice == null, p.LowestPrice ascending หวังว่าจะช่วยคน
shaijut

@DaveShaw ขอบคุณสำหรับเคล็ดลับ - โดยเฉพาะอย่างยิ่งความคิดเห็นที่หนึ่ง - เป็นระเบียบเรียบร้อยมาก - รักมัน
Demetris Leptos

86

มันจะช่วยให้เข้าใจไวยากรณ์คิวรี LINQ และวิธีการแปลเป็นการเรียกเมธอด LINQ ได้อย่างไร

ปรากฎว่า

var products = from p in _context.Products
               where p.ProductTypeId == 1
               orderby p.LowestPrice.HasValue descending
               orderby p.LowestPrice descending
               select p;

จะถูกแปลโดยคอมไพเลอร์เป็น

var products = _context.Products
                       .Where(p => p.ProductTypeId == 1)
                       .OrderByDescending(p => p.LowestPrice.HasValue)
                       .OrderByDescending(p => p.LowestPrice)
                       .Select(p => p);

นี่ไม่ใช่สิ่งที่คุณต้องการอย่างชัดเจน ประเภทนี้โดยProduct.LowestPrice.HasValueในdescendingการสั่งซื้อและจากนั้นอีกครั้งจะเรียงลำดับการเก็บรวบรวมทั้งหมดโดยProduct.LowestPriceในdescendingการสั่งซื้อ

สิ่งที่คุณต้องการคือ

var products = _context.Products
                       .Where(p => p.ProductTypeId == 1)
                       .OrderByDescending(p => p.LowestPrice.HasValue)
                       .ThenBy(p => p.LowestPrice)
                       .Select(p => p);

ซึ่งคุณสามารถรับได้โดยใช้ไวยากรณ์แบบสอบถามโดย

var products = from p in _context.Products
               where p.ProductTypeId == 1
               orderby p.LowestPrice.HasValue descending,
                       p.LowestPrice
               select p;

สำหรับรายละเอียดของการแปลจากไวยากรณ์เคียวรีไปยังการเรียกเมธอดดูข้อกำหนดภาษา อย่างจริงจัง. อ่านมัน


4
+1 หรือเพียง ... อย่าเขียนไวยากรณ์
คิวรี

18

วิธีแก้ปัญหาสำหรับค่าสตริงนั้นแปลกจริง ๆ :

.OrderBy(f => f.SomeString == null).ThenBy(f => f.SomeString) 

เหตุผลเดียวที่ทำงานเป็นเพราะการแสดงออกแรกOrderBy()เรียงลำดับค่า:bool / ผลไปก่อนตามด้วยผลลัพธ์ (nullables) และเรียงลำดับค่าที่ไม่ใช่ null ตามตัวอักษรtruefalsefalsetrueThenBy()

ดังนั้นฉันชอบทำสิ่งที่อ่านได้มากกว่านี้เช่นนี้:

.OrderBy(f => f.SomeString ?? "z")

ถ้าSomeStringเป็นโมฆะมันจะถูกแทนที่ด้วยจาก"z"นั้นเรียงลำดับทุกอย่างตามตัวอักษร

หมายเหตุ: นี้ไม่ได้เป็นทางออกที่ดีที่สุดตั้งแต่"z"ไปก่อนกว่า zebraZ-ค่าเช่น

อัปเดต 9/6/2559 - เกี่ยวกับ @jornhd ความคิดเห็นมันเป็นทางออกที่ดี แต่มันก็ยังซับซ้อนอยู่เล็กน้อยดังนั้นฉันจะแนะนำให้ห่อในชั้นเรียนส่วนขยายเช่นนี้:

public static class MyExtensions
{
    public static IOrderedEnumerable<T> NullableOrderBy<T>(this IEnumerable<T> list, Func<T, string> keySelector)
    {
        return list.OrderBy(v => keySelector(v) != null ? 0 : 1).ThenBy(keySelector);
    }
}

และใช้ง่ายเหมือน:

var sortedList = list.NullableOrderBy(f => f.SomeString);

2
ฉันคิดว่านี่อ่านง่ายกว่าโดยไม่มีค่าคงที่ที่น่ารังเกียจ:. OrderBy (f => f.SomeString! = null? 0: 1) .ThBy (f => f.SomeString)
jornhd

14

ฉันมีตัวเลือกอื่นในสถานการณ์เช่นนี้ รายการของฉันคือ objList และฉันต้องสั่ง แต่ null จะต้องสิ้นสุด การตัดสินใจของฉัน:

var newList = objList.Where(m=>m.Column != null)
                     .OrderBy(m => m.Column)
                     .Concat(objList.where(m=>m.Column == null));

สิ่งนี้สามารถทำงานในสถานการณ์ที่ต้องการผลลัพธ์ด้วยค่าอื่น ๆ เช่น 0 แทนที่ null
Naresh Ravlani

ใช่. เพียงแทนที่ null เป็น 0
Gurgen Hovsepyan

นี่เป็นคำตอบเดียวที่เหมาะกับฉันส่วนที่เหลือจะเก็บค่า null ไว้ในตอนต้นของรายการ
BMills

9

ฉันพยายามหาคำตอบของ LINQ แต่ไม่สามารถหาคำตอบได้ที่นี่

คำตอบสุดท้ายของฉันคือ:

.OrderByDescending(p => p.LowestPrice.HasValue).ThenBy(p => p.LowestPrice)


7

นี่คือสิ่งที่ฉันเกิดขึ้นเพราะฉันใช้วิธีการขยายและรายการของฉันเป็นสตริงจึงไม่มี.HasValue:

.OrderBy(f => f.SomeString == null).ThenBy(f => f.SomeString)

ใช้งานได้กับวัตถุ LINQ 2 ในหน่วยความจำ ฉันไม่ได้ทดสอบกับ EF หรือ DB ORM ใด ๆ


0

ด้านล่างเป็นวิธีส่วนขยายเพื่อตรวจสอบค่าว่างหากคุณต้องการเรียงลำดับบนคุณสมบัติย่อยของ keySelector

public static IOrderedEnumerable<T> NullableOrderBy<T>(this IEnumerable<T> list, Func<T, object> parentKeySelector, Func<T, object> childKeySelector)
{
    return list.OrderBy(v => parentKeySelector(v) != null ? 0 : 1).ThenBy(childKeySelector);
}

และใช้ง่ายเหมือน:

var sortedList = list.NullableOrderBy(x => x.someObject, y => y.someObject?.someProperty);

0

นี่เป็นอีกวิธีหนึ่ง:

//Acsending
case "SUP_APPROVED_IND": qry =
                            qry.OrderBy(r => r.SUP_APPROVED_IND.Trim() == null).
                                    ThenBy(r => r.SUP_APPROVED_IND);

                            break;
//….
//Descending
case "SUP_APPROVED_IND": qry =
                            qry.OrderBy(r => r.SUP_APPROVED_IND.Trim() == null).
                                    ThenByDescending(r => r.SUP_APPROVED_IND); 

                            break;

SUP_APPROVED_IND is char(1) in Oracle db.

โปรดทราบว่าr.SUP_APPROVED_IND.Trim() == nullถือว่าเป็นเช่นเดียวกับtrim(SUP_APPROVED_IND) is nullใน Oracle db

ดูสิ่งนี้สำหรับรายละเอียด: ฉันจะค้นหาค่าว่างในกรอบงานเอนทิตีได้อย่างไร


0

ตัวเลือกอื่น (สะดวกสำหรับสถานการณ์ของเรา):

เรามีตารางผู้ใช้จัดเก็บ ADName, LastName, FirstName

  • ผู้ใช้ควรเป็นตัวอักษร
  • บัญชีที่ไม่มี First- / นามสกุลเช่นกันขึ้นอยู่กับ ADName ของพวกเขา - แต่ในตอนท้ายของรายการผู้ใช้
  • ผู้ใช้หลอกตาด้วย ID "0" ("ไม่มีตัวเลือก") ควรอยู่ด้านบนสุดเสมอ

เราเปลี่ยน schema ของตารางและเพิ่มคอลัมน์ "SortIndex" ซึ่งกำหนดกลุ่มการเรียงลำดับบางอย่าง (เราเว้นช่องว่าง 5 ดังนั้นเราจึงสามารถแทรกกลุ่มได้ในภายหลัง)

ID | ADName |      First Name | LastName | SortIndex
0    No Selection  null         null     | 0
1    AD\jon        Jon          Doe      | 5
3    AD\Support    null         null     | 10     
4    AD\Accounting null         null     | 10
5    AD\ama        Amanda       Whatever | 5

ตอนนี้แบบสอบถามที่ชาญฉลาดมันจะเป็น:

SELECT * FROM User order by SortIndex, LastName, FirstName, AdName;

ในวิธีการแสดงออก:

db.User.OrderBy(u => u.SortIndex).ThenBy(u => u.LastName).ThenBy(u => u.FirstName).ThenBy(u => u.AdName).ToList();

ซึ่งให้ผลที่คาดหวัง:

ID | ADName |      First Name | LastName | SortIndex
0    No Selection  null         null     | 0
5    AD\ama        Amanda       Whatever | 5
1    AD\jon        Jon          Doe      | 5
4    AD\Accounting null         null     | 10
3    AD\Support    null         null     | 10     
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.