LINQ ไปยังหน่วยงานไม่รู้จักวิธีการล่าสุด จริงๆ?


144

ในแบบสอบถามนี้:

public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
    var context = DataContext.GetDataContext();
    return context.ServerOnlineCharacters
        .OrderBy(p => p.ServerStatus.ServerDateTime)
        .GroupBy(p => p.RawName)
        .Select(p => p.Last());
}

ฉันต้องเปลี่ยนมันเพื่อใช้งานได้

public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
    var context = DataContext.GetDataContext();
    return context.ServerOnlineCharacters
        .OrderByDescending(p => p.ServerStatus.ServerDateTime)
        .GroupBy(p => p.RawName)
        .Select(p => p.FirstOrDefault());
}

ฉันไม่สามารถใช้p.First()เพื่อสะท้อนคำถามแรก

เหตุใดจึงมีข้อ จำกัด พื้นฐานในระบบ ORM ที่แข็งแกร่งเช่นนั้น


เก็บวัตถุ IEnumrable ของคุณลงในตัวแปรใหม่จากนั้นคืนค่า variable.last () มันจะทำงาน.
Ali.Rashidi

คำตอบ:


220

ข้อ จำกัด นั้นเกิดขึ้นกับความจริงที่ว่าในที่สุดมันก็มีการแปลแบบสอบถามเป็นSQLและ SQL มีSELECT TOP(ใน T-SQL) แต่ไม่ใช่SELECT BOTTOM(ไม่มีสิ่งนั้น)

มีวิธีง่าย ๆ อยู่รอบข้างเพียงแค่เรียงลำดับจากมากไปน้อยแล้วทำFirst()ซึ่งก็คือสิ่งที่คุณทำ

แก้ไข: ผู้ให้บริการรายอื่นอาจจะมีการใช้งานที่แตกต่างกันSELECT TOP 1บน Oracle มันอาจจะเป็นอะไรที่มากกว่าWHERE ROWNUM = 1

แก้ไข:

อีกทางเลือกที่มีประสิทธิภาพน้อยกว่า - ฉันไม่แนะนำนี้! - คือการเรียก.ToList()ใช้ข้อมูลของคุณก่อน.Last()ซึ่งจะดำเนินการทันที LINQ ไปยังหน่วยงานการแสดงออกที่ถูกสร้างขึ้นมาถึงจุดนั้นแล้ว. Last () ของคุณจะทำงานเพราะ ณ จุดนั้น.Last()จะดำเนินการได้อย่างมีประสิทธิภาพในบริบทของLINQ ไปยังนิพจน์วัตถุแทน (และในขณะที่คุณชี้ให้เห็นมันสามารถดึงข้อมูลหลายพันรายการกลับมาและทำให้โหลดวัตถุที่เป็นรูปธรรมของ CPU ที่ไม่เคยถูกใช้งานได้)

อีกครั้งฉันจะไม่แนะนำให้ทำในวินาทีนี้ แต่มันจะช่วยแสดงให้เห็นถึงความแตกต่างระหว่างที่ไหนและเมื่อมีการดำเนินการแสดงออกของ LINQ


และ LINQ To SQL จัดการกับสถานการณ์นี้อย่างไร?
bevacqua

@Neil ใช่ฉันรู้ว่าฉันไม่สามารถโทร ToList แต่ฉันไม่อยากดึงพันของระเบียนจากฐานข้อมูลเพียงเพื่อกรองพวกเขาลงไปห้าบันทึก
Bevacqua

2
หากคุณรู้ว่าข้อความค้นหาของคุณกำลังจะส่งกลับผลลัพธ์ขนาดเล็กการโทรToListไม่ได้เลวร้ายอะไร
Justin Skiles


14

แทนที่Last()ด้วยตัวเลือก LinqOrderByDescending(x => x.ID).Take(1).Single()

บางอย่างเช่นนี้จะใช้ได้ถ้าคุณต้องการทำใน Linq:

public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
    var context = DataContext.GetDataContext();
    return context.ServerOnlineCharacters.OrderBy(p => p.ServerStatus.ServerDateTime).GroupBy(p => p.RawName).Select(p => p.OrderByDescending(x => x.Id).Take(1).Single());
}

1
มีเหตุผลใดที่จะใช้ใช้ (1) .Single () แทนที่จะเป็น. FirstOrDefault ()
Tot Zam

2
@TotZam การแทนที่ที่ถูกต้องจะเป็น. First () ในกรณีนั้นเนื่องจาก Single () ส่งข้อยกเว้นหากจำนวนรายการไม่ตรง 1
MEMark

0

วิธีอื่นรับองค์ประกอบสุดท้ายโดยไม่มี OrderByDescending และโหลดเอนทิตีทั้งหมด:

dbSet
    .Where(f => f.Id == dbSet.Max(f2 => f2.Id))
    .FirstOrDefault();

0

นั่นเป็นเพราะ LINQ ไปยังเอนทิตี (และฐานข้อมูลโดยทั่วไป) ไม่สนับสนุนเมธอด LINQ ทั้งหมด (ดูที่นี่สำหรับรายละเอียด: http://msdn.microsoft.com/en-us/library/bb738550.aspx )

สิ่งที่คุณต้องการที่นี่คือการสั่งซื้อข้อมูลของคุณในลักษณะที่ว่า "บันทึก" ครั้งสุดท้ายกลายเป็น "ครั้งแรก" จากนั้นคุณสามารถใช้ FirstOrDefault โปรดทราบว่า databasese มักจะไม่มีแนวคิดเช่น "ครั้งแรก" และ "สุดท้าย" มันไม่เหมือนกับเร็กคอร์ดที่แทรกล่าสุดจะเป็น "ล่าสุด" ในตาราง

วิธีนี้สามารถแก้ปัญหาของคุณได้

db.databaseTable.OrderByDescending(obj => obj.Id).FirstOrDefault();

-2

การเพิ่มฟังก์ชั่นเดียวAsEnumerable()ก่อนที่ฟังก์ชั่น Select จะทำงานให้ฉัน
ตัวอย่าง:

return context.ServerOnlineCharacters
    .OrderByDescending(p => p.ServerStatus.ServerDateTime)
    .GroupBy(p => p.RawName).AsEnumerable()
    .Select(p => p.FirstOrDefault());

Ref: https://www.codeproject.com/Questions/1005274/LINQ-to-Entities-does-not-recognize-the-method-Sys


ขอแนะนำให้รวมรหัสที่ใช้งานได้จากลิงก์ในคำตอบของคุณ คำตอบสำหรับลิงค์เท่านั้นจะดึงดูดความสนใจในเชิงลบ โปรดระบุคำตอบแบบเต็มโดยเพิ่มรหัสที่คุณพบเพื่อช่วยและแก้ปัญหา วิธีนี้จะช่วยแก้ปัญหาลิงก์ที่ไม่ทำงานเนื่องจากข้อผิดพลาด 404 ข้อในอนาคต
Studocwho

1
เพิ่มตัวอย่างในคำตอบของฉัน
Artem Levitin

ข้อเสียของคำตอบนี้คือจะนำผลลัพธ์ทั้งหมดมาก่อนด้านเซิร์ฟเวอร์ "AsEnumerable" จากนั้นเลือกอันแรก สิ่งนี้อาจไม่เป็นที่พึงปรารถนา (ฉันมีสถานการณ์เช่นนี้ซึ่งผลลัพธ์ใช้เวลา 20+ วินาทีเนื่องจากมีการบันทึก 20k + ที่นำเซิร์ฟเวอร์ฝั่งเมื่อฉันย้ายมันกลับด้าน DB ผลลัพธ์ที่ส่งคืนในเวลาน้อยกว่าหนึ่งวินาที)
TChadwick
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.