LINQ Single vs First


215

LINQ:

จะมีประสิทธิภาพมากกว่าหรือไม่หากใช้ตัวSingle()ดำเนินการมากกว่าFirst()เมื่อรู้แน่ ๆ ว่าแบบสอบถามจะส่งคืนเรกคอร์ดเดียวหรือไม่

มีความแตกต่างหรือไม่?

คำตอบ:


312

หากคุณคาดหวังว่าจะมีการบันทึกเพียงครั้งเดียวก็เป็นการดีที่จะระบุไว้อย่างชัดเจนในรหัสของคุณ

ฉันรู้ว่าคนอื่นเขียนทำไมคุณใช้อย่างใดอย่างหนึ่ง แต่ฉันคิดว่าฉันแสดงให้เห็นว่าทำไมคุณไม่ควรใช้อย่างใดอย่างหนึ่งเมื่อคุณหมายถึงอื่น ๆ

หมายเหตุ: ในรหัสของฉันฉันมักจะใช้FirstOrDefault()และSingleOrDefault()แต่นั่นเป็นคำถามที่แตกต่างกัน

ยกตัวอย่างเช่นตารางที่จัดเก็บCustomersในภาษาต่าง ๆ โดยใช้คีย์คอมโพสิต ( ID, Lang):

DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 ).First();

รหัสข้างต้นนี้แนะนำข้อผิดพลาดทางตรรกะที่เป็นไปได้ (ยากต่อการติดตาม) มันจะส่งคืนมากกว่าหนึ่งระเบียน (สมมติว่าคุณมีบันทึกลูกค้าในหลายภาษา) แต่จะส่งคืนเฉพาะระเบียนแรกเท่านั้น ... ซึ่งอาจทำงานได้ในบางครั้ง ... แต่ไม่ใช่อื่น ๆ มันคาดเดาไม่ได้

เนื่องจากความตั้งใจของคุณคือการคืนการCustomerใช้งานครั้งเดียวSingle();

ข้อยกเว้นต่อไปนี้จะทำให้เกิดข้อยกเว้น (ซึ่งเป็นสิ่งที่คุณต้องการในกรณีนี้):

DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 ).Single();

จากนั้นคุณก็ตีที่หน้าผากและพูดกับตัวเอง ... OOPS! ฉันลืมฟิลด์ภาษา! ต่อไปนี้เป็นรุ่นที่ถูกต้อง:

DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 && c.Lang == "en" ).Single();

First() มีประโยชน์ในสถานการณ์ต่อไปนี้:

DBContext db = new DBContext();
NewsItem newsitem = db.NewsItems.OrderByDescending( n => n.AddedDate ).First();

มันจะคืนค่าหนึ่งวัตถุและเนื่องจากคุณใช้การเรียงลำดับมันจะเป็นระเบียนล่าสุดที่ส่งคืน

การใช้Single()เมื่อคุณรู้สึกว่าควรส่งคืน 1 ระเบียนอย่างชัดเจนเสมอจะช่วยให้คุณหลีกเลี่ยงข้อผิดพลาดเชิงตรรกะ


76
ทั้งเมธอด Single และ First สามารถใช้พารามิเตอร์ expression สำหรับการกรองได้ดังนั้นไม่จำเป็นต้องใช้ฟังก์ชั่น Where ตัวอย่าง: ลูกค้าลูกค้า = db.Customers.Single (c => c.ID == 5);
Josh Noe

6
@JoshNoe - ฉันอยากรู้อยากเห็นมีความแตกต่างระหว่างcustomers.Where(predicate).Single() customers.Single(predicate)?
drzaus

9
@drzaus - ตามหลักเหตุผลไม่มีทั้งคู่กรองค่าที่จะส่งคืนตามภาคแสดง อย่างไรก็ตามฉันตรวจสอบการถอดแยกชิ้นส่วนและ Where (predicate) .Single () มีคำแนะนำพิเศษสามอย่างในกรณีง่าย ๆ ที่ฉันทำ ดังนั้นในขณะที่ฉันไม่มีผู้เชี่ยวชาญ IL แต่ปรากฏว่าลูกค้าโสด (ภาคแสดง) ควรมีประสิทธิภาพมากขึ้น
Josh Noe

5
@JoshNoe มันค่อนข้างตรงข้ามกับที่ปรากฏออกมา
AgentFire


72

Single จะส่งข้อยกเว้นหากพบมากกว่าหนึ่งระเบียนที่ตรงกับเกณฑ์ อันดับแรกจะเลือกระเบียนแรกจากรายการเสมอ ถ้าแบบสอบถามส่งกลับเพียง 1 รายการ, First()คุณสามารถไปกับ

ทั้งสองจะโยนInvalidOperationExceptionข้อยกเว้นถ้าคอลเลกชันว่างเปล่า SingleOrDefault()หรือคุณสามารถใช้ สิ่งนี้จะไม่ส่งข้อยกเว้นหากรายการว่างเปล่า


29

เดี่ยว ()

ส่งคืนองค์ประกอบเฉพาะของแบบสอบถาม

เมื่อใช้ : หากคาดหวัง 1 องค์ประกอบ ไม่ใช่ 0 หรือมากกว่า 1 หากรายการว่างเปล่าหรือมีองค์ประกอบมากกว่าหนึ่งรายการมันจะส่งข้อยกเว้น "ลำดับประกอบด้วยมากกว่าหนึ่งองค์ประกอบ"

SingleOrDefault ()

ส่งคืนองค์ประกอบเฉพาะของแบบสอบถามหรือค่าเริ่มต้นหากไม่พบผลลัพธ์

เมื่อใช้ : คาดว่าจะมีองค์ประกอบ 0 หรือ 1 มันจะโยนข้อยกเว้นถ้ารายการมี 2 รายการขึ้นไป

แรก ()

ส่งคืนองค์ประกอบแรกของแบบสอบถามที่มีหลายผลลัพธ์

เมื่อใช้ : เมื่อคาดว่าจะมี 1 องค์ประกอบขึ้นไปและคุณต้องการเพียงองค์ประกอบแรกเท่านั้น มันจะโยนข้อยกเว้นหากรายการไม่มีองค์ประกอบ

FirstOrDefault ()

ส่งคืนองค์ประกอบแรกของรายการที่มีจำนวนขององค์ประกอบใด ๆ หรือค่าเริ่มต้นหากรายการว่างเปล่า

เมื่อใช้ : เมื่อคาดว่าจะมีหลายองค์ประกอบและคุณต้องการเพียงองค์ประกอบแรกเท่านั้น default(MyObjectType)หรือรายการที่ยังว่างอยู่และคุณต้องการค่าเริ่มต้นสำหรับประเภทที่ระบุเช่นเดียวกับ ตัวอย่างเช่นหากประเภทรายการlist<int>จะคืนค่าหมายเลขแรกจากรายการหรือ 0 หากรายการว่างเปล่า ถ้าเป็นlist<string>มันจะส่งคืนสตริงแรกจากรายการหรือ null หากรายการว่างเปล่า


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

18

มีความแตกต่างที่ลึกซึ้งและความหมายระหว่างวิธีการทั้งสองนี้

ใช้Singleเพื่อดึงองค์ประกอบแรก (และเท่านั้น) จากลำดับที่ควรมีองค์ประกอบหนึ่งและไม่มาก หากลำดับมีมากกว่าองค์ประกอบการเรียกร้องของคุณSingleจะทำให้เกิดข้อยกเว้นเนื่องจากคุณระบุว่าควรมีองค์ประกอบเดียวเท่านั้น

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

หากลำดับไม่มีองค์ประกอบที่ทั้งสองวิธีการเรียกจะทำให้เกิดข้อยกเว้นที่จะโยนเพราะทั้งสองวิธีคาดว่าจะมีอย่างน้อยหนึ่งองค์ประกอบที่จะนำเสนอ


17

หากคุณไม่ได้ต้องการเฉพาะข้อยกเว้นโยนในกรณีที่มีรายการมากกว่าหนึ่งการใช้งานFirst()

ทั้งสองอย่างมีประสิทธิภาพใช้รายการแรก First()มีประสิทธิภาพมากกว่าเล็กน้อยเนื่องจากไม่ได้ตรวจสอบเพื่อดูว่ามีรายการที่สองหรือไม่

ความแตกต่างเพียงอย่างเดียวคือSingle()คาดว่าจะมีเพียงหนึ่งรายการในการแจงนับที่จะเริ่มต้นด้วยและจะส่งข้อยกเว้นหากมีมากกว่าหนึ่งรายการ คุณใช้ .Single() ถ้าคุณต้องการข้อยกเว้นโดยเฉพาะในกรณีนี้


14

ถ้าฉันจำได้ว่า Single () ตรวจสอบว่ามีองค์ประกอบอื่นหลังจากองค์ประกอบแรก (และส่งข้อยกเว้นถ้าเป็นกรณีนั้น) ในขณะที่ First () หยุดหลังจากได้รับ ทั้งโยนข้อยกเว้นถ้าลำดับว่างเปล่า

ส่วนตัวฉันใช้ First () เสมอ


2
ใน SQL พวกเขาสร้าง First () ทำ TOP 1 และ Single () ทำ TOP 2 ถ้าฉันไม่ผิด
Matthijs Wessels

10

เกี่ยวกับการแสดง: เพื่อนร่วมงานและฉันกำลังพูดคุยเกี่ยวกับประสิทธิภาพการทำงานของ Single vs First (หรือ SingleOrDefault vs FirstOrDefault) และฉันก็เถียงกันว่าประเด็นแรก (หรือ FirstOrDefault) จะเร็วขึ้นและปรับปรุงประสิทธิภาพ (ฉันทั้งหมดเกี่ยวกับการสร้างแอปของเรา วิ่งเร็วกว่า).

ฉันได้อ่านบทความหลายเรื่องเกี่ยวกับ Stack Overflow ที่ถกเถียงกันเรื่องนี้ บางคนบอกว่ามีประสิทธิภาพเพิ่มขึ้นเล็กน้อยเมื่อใช้ First แทนที่จะเป็น Single นี่เป็นเพราะอันดับแรกจะส่งคืนรายการแรกในขณะที่ Single ต้องสแกนผลลัพธ์ทั้งหมดเพื่อให้แน่ใจว่าไม่มีรายการที่ซ้ำกัน (เช่น: หากพบรายการในแถวแรกของตารางจะยังคงสแกนแถวอื่นทุกแถวเพื่อ ตรวจสอบให้แน่ใจว่าไม่มีค่าที่สองที่ตรงกับเงื่อนไขซึ่งจะทำให้เกิดข้อผิดพลาด) ฉันรู้สึกว่าฉันอยู่บนพื้นแข็งด้วยการ "เร็ว" เร็วกว่า "ซิงเกิล" ดังนั้นฉันจึงออกเดินทางเพื่อพิสูจน์และวางการอภิปรายเพื่อพักผ่อน

ฉันตั้งค่าการทดสอบในฐานข้อมูลของฉันและเพิ่ม 1,000,000 แถวของ ID UniqueIdentifier ข้อมูลต่างประเทศ UniqueIdentifier nvarchar (50) (เต็มไปด้วยสตริงตัวเลข“ 0” ถึง“ 999,9999”

ฉันโหลดข้อมูลและตั้งค่า ID เป็นเขตข้อมูลคีย์หลัก

ใช้ LinqPad เป้าหมายของฉันคือการแสดงให้เห็นว่าหากคุณค้นหาค่าใน 'ต่างประเทศ' หรือ 'ข้อมูล' โดยใช้ซิงเกิลมันจะแย่กว่าการใช้ First มาก

ฉันไม่สามารถอธิบายผลลัพธ์ที่ได้ ในเกือบทุกกรณีการใช้ Single หรือ SingleOrDefault นั้นเร็วกว่าเล็กน้อย สิ่งนี้ไม่สมเหตุสมผลกับฉัน แต่ฉันต้องการแบ่งปันสิ่งนั้น

เช่นฉันใช้ข้อความค้นหาต่อไปนี้:

var q = TestTables.First(x=>x.Info == "314638") ;
//Vs.
Var q = TestTables.Single(x=>x.Info =="314638") ; //(this was slightly faster to my surprise)

ฉันลองใช้คำค้นหาที่คล้ายกันในฟิลด์สำคัญ 'ต่างประเทศ' ซึ่งไม่ได้จัดทำดัชนีความคิดที่จะพิสูจน์ว่า First เร็วกว่า แต่ Single ได้เร็วขึ้นเล็กน้อยในการทดสอบของฉัน


2
หากอยู่ในเขตข้อมูลที่มีการจัดทำดัชนีมากกว่าฐานข้อมูลไม่จำเป็นต้องทำการสแกนเพื่อให้แน่ใจว่าเป็นค่าที่ไม่ซ้ำ มันรู้แล้วว่ามันเป็น ดังนั้นจึงไม่มีค่าใช้จ่ายที่นั่นและค่าใช้จ่ายเพียงอย่างเดียวในฝั่งเซิร์ฟเวอร์คือการรับประกันว่ามีเพียงหนึ่งระเบียนที่กลับมา การทดสอบประสิทธิภาพเป็นการทดสอบตัวเองไม่ใช่ข้อสรุปเดียวหรืออย่างอื่น
mirhagk

1
ฉันไม่คิดว่าผลลัพธ์เหล่านี้จะเหมือนกันบนวัตถุที่ซับซ้อนหากคุณค้นหาในฟิลด์ที่ไม่ได้ใช้โดย IComparer
Anthony Nichols

นั่นควรเป็นคำถามอื่น ให้แน่ใจว่ามีแหล่งที่มาของคุณทดสอบ
Sinatr

5

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


5

คุณสามารถลองตัวอย่างง่ายๆเพื่อรับความแตกต่าง ข้อยกเว้นจะถูกโยนในบรรทัดที่ 3;

        List<int> records = new List<int>{1,1,3,4,5,6};
        var record = records.First(x => x == 1);
        record = records.Single(x => x == 1);

3

ผู้คนจำนวนมากที่ฉันรู้จักใช้ FirstOrDefault () แต่ฉันมักจะใช้ SingleOrDefault () มากขึ้นเพราะบ่อยครั้งที่มันจะเป็นข้อมูลที่ไม่สอดคล้องกันหากมีมากกว่าหนึ่ง นี่เป็นการจัดการกับ LINQ-to-Objects


-1

บันทึกในนิติบุคคลพนักงาน:

Employeeid = 1: พนักงานเพียงคนเดียวที่มี ID นี้

Firstname = Robert: พนักงานมากกว่าหนึ่งคนที่ใช้ชื่อนี้

Employeeid = 10: ไม่มีพนักงานที่มี ID นี้

ตอนนี้มันจำเป็นที่จะต้องเข้าใจในสิ่งที่Single()และFirst()รายละเอียดในรายละเอียด

เดี่ยว ()

Single () ใช้เพื่อส่งคืนเรกคอร์ดเดียวที่มีอยู่ในตารางดังนั้นแบบสอบถามด้านล่างจะส่งคืนพนักงานที่มีemployeed =1เพราะเรามีพนักงานเพียงคนเดียวที่มีEmployeed1 หากเรามีสองระเบียนสำหรับEmployeeId = 1มันจะเกิดข้อผิดพลาด (ดู Firstnameข้อผิดพลาดดังต่อไปนี้ในแบบสอบถามที่สองที่เราจะใช้ตัวอย่างสำหรับ

Employee.Single(e => e.Employeeid == 1)

ด้านบนจะส่งคืนระเบียนเดียวซึ่งมี 1 employeeId

Employee.Single(e => e.Firstname == "Robert")

ข้างต้นจะโยนข้อยกเว้นเพราะบันทึก multilple FirstName='Robert'อยู่ในตารางสำหรับ ข้อยกเว้นจะเป็น

InvalidOperationException: ลำดับประกอบด้วยมากกว่าหนึ่งองค์ประกอบ

Employee.Single(e => e.Employeeid == 10)

สิ่งนี้จะทำให้เกิดข้อผิดพลาดอีกครั้งเนื่องจากไม่มีระเบียนสำหรับ id = 10 ข้อยกเว้นจะเป็น

InvalidOperationException: ลำดับไม่มีองค์ประกอบ

เพราะEmployeeId = 10มันจะกลับมาเป็นโมฆะ แต่ในขณะที่เราใช้Single()มันจะโยนข้อผิดพลาด เพื่อที่จะจับ null SingleOrDefault()ข้อผิดพลาดเราควรใช้

แรก ()

First () ผลตอบแทนจากหลายระเบียนระเบียนที่สอดคล้องกันเรียงลำดับจากน้อยไปมากbirthdateดังนั้นมันจะส่งคืน 'Robert' ที่เก่าที่สุด

Employee.OrderBy(e => e. Birthdate)
.First(e => e.Firstname == "Robert")

ด้านบนควรส่งคืน Robert ที่เก่าแก่ที่สุดตาม DOB

Employee.OrderBy(e => e. Birthdate)
.First(e => e.Employeeid == 10)

ด้านบนจะส่งข้อยกเว้นเนื่องจากไม่มีระเบียนสำหรับ id = 10 เพื่อหลีกเลี่ยงข้อยกเว้น null เราควรใช้มากกว่าFirstOrDefault()First()

หมายเหตุ: เราสามารถใช้First()/ Single()เมื่อเรามั่นใจอย่างยิ่งว่ามันไม่สามารถคืนค่าเป็นศูนย์

ในทั้งสองฟังก์ชั่นใช้SingleOrDefault () หรือ FirstOrDefault ()ซึ่งจะจัดการกับข้อยกเว้นที่เป็นโมฆะในกรณีที่ไม่มีบันทึกพบว่ามันจะกลับมาเป็นโมฆะ


กรุณาอธิบายคำตอบของคุณ
MrMaavin

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