LINQ:
จะมีประสิทธิภาพมากกว่าหรือไม่หากใช้ตัวSingle()
ดำเนินการมากกว่าFirst()
เมื่อรู้แน่ ๆ ว่าแบบสอบถามจะส่งคืนเรกคอร์ดเดียวหรือไม่
มีความแตกต่างหรือไม่?
LINQ:
จะมีประสิทธิภาพมากกว่าหรือไม่หากใช้ตัวSingle()
ดำเนินการมากกว่าFirst()
เมื่อรู้แน่ ๆ ว่าแบบสอบถามจะส่งคืนเรกคอร์ดเดียวหรือไม่
มีความแตกต่างหรือไม่?
คำตอบ:
ฉันรู้ว่าคนอื่นเขียนทำไมคุณใช้อย่างใดอย่างหนึ่ง แต่ฉันคิดว่าฉันแสดงให้เห็นว่าทำไมคุณไม่ควรใช้อย่างใดอย่างหนึ่งเมื่อคุณหมายถึงอื่น ๆ
หมายเหตุ: ในรหัสของฉันฉันมักจะใช้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 ระเบียนอย่างชัดเจนเสมอจะช่วยให้คุณหลีกเลี่ยงข้อผิดพลาดเชิงตรรกะ
customers.Where(predicate).Single()
customers.Single(predicate)
?
Single จะส่งข้อยกเว้นหากพบมากกว่าหนึ่งระเบียนที่ตรงกับเกณฑ์ อันดับแรกจะเลือกระเบียนแรกจากรายการเสมอ ถ้าแบบสอบถามส่งกลับเพียง 1 รายการ, First()
คุณสามารถไปกับ
ทั้งสองจะโยนInvalidOperationException
ข้อยกเว้นถ้าคอลเลกชันว่างเปล่า SingleOrDefault()
หรือคุณสามารถใช้ สิ่งนี้จะไม่ส่งข้อยกเว้นหากรายการว่างเปล่า
เดี่ยว ()
ส่งคืนองค์ประกอบเฉพาะของแบบสอบถาม
เมื่อใช้ : หากคาดหวัง 1 องค์ประกอบ ไม่ใช่ 0 หรือมากกว่า 1 หากรายการว่างเปล่าหรือมีองค์ประกอบมากกว่าหนึ่งรายการมันจะส่งข้อยกเว้น "ลำดับประกอบด้วยมากกว่าหนึ่งองค์ประกอบ"
SingleOrDefault ()
ส่งคืนองค์ประกอบเฉพาะของแบบสอบถามหรือค่าเริ่มต้นหากไม่พบผลลัพธ์
เมื่อใช้ : คาดว่าจะมีองค์ประกอบ 0 หรือ 1 มันจะโยนข้อยกเว้นถ้ารายการมี 2 รายการขึ้นไป
แรก ()
ส่งคืนองค์ประกอบแรกของแบบสอบถามที่มีหลายผลลัพธ์
เมื่อใช้ : เมื่อคาดว่าจะมี 1 องค์ประกอบขึ้นไปและคุณต้องการเพียงองค์ประกอบแรกเท่านั้น มันจะโยนข้อยกเว้นหากรายการไม่มีองค์ประกอบ
FirstOrDefault ()
ส่งคืนองค์ประกอบแรกของรายการที่มีจำนวนขององค์ประกอบใด ๆ หรือค่าเริ่มต้นหากรายการว่างเปล่า
เมื่อใช้ : เมื่อคาดว่าจะมีหลายองค์ประกอบและคุณต้องการเพียงองค์ประกอบแรกเท่านั้น
default(MyObjectType)
หรือรายการที่ยังว่างอยู่และคุณต้องการค่าเริ่มต้นสำหรับประเภทที่ระบุเช่นเดียวกับ ตัวอย่างเช่นหากประเภทรายการlist<int>
จะคืนค่าหมายเลขแรกจากรายการหรือ 0 หากรายการว่างเปล่า ถ้าเป็นlist<string>
มันจะส่งคืนสตริงแรกจากรายการหรือ null หากรายการว่างเปล่า
First
เมื่อคาดว่าจะมี 1 องค์ประกอบขึ้นไปไม่ใช่แค่ "มากกว่า 1" และFirstOrDefault
ด้วยองค์ประกอบจำนวนเท่าใดก็ได้
มีความแตกต่างที่ลึกซึ้งและความหมายระหว่างวิธีการทั้งสองนี้
ใช้Single
เพื่อดึงองค์ประกอบแรก (และเท่านั้น) จากลำดับที่ควรมีองค์ประกอบหนึ่งและไม่มาก หากลำดับมีมากกว่าองค์ประกอบการเรียกร้องของคุณSingle
จะทำให้เกิดข้อยกเว้นเนื่องจากคุณระบุว่าควรมีองค์ประกอบเดียวเท่านั้น
ใช้First
เพื่อดึงองค์ประกอบแรกจากลำดับที่สามารถมีองค์ประกอบจำนวนเท่าใดก็ได้ หากลำดับมีมากกว่าองค์ประกอบการเรียกร้องของคุณFirst
จะไม่ทำให้เกิดข้อยกเว้นเนื่องจากคุณระบุว่าคุณต้องการองค์ประกอบแรกในลำดับเท่านั้นและไม่สนใจว่ามีอยู่จริงหรือไม่
หากลำดับไม่มีองค์ประกอบที่ทั้งสองวิธีการเรียกจะทำให้เกิดข้อยกเว้นที่จะโยนเพราะทั้งสองวิธีคาดว่าจะมีอย่างน้อยหนึ่งองค์ประกอบที่จะนำเสนอ
หากคุณไม่ได้ต้องการเฉพาะข้อยกเว้นโยนในกรณีที่มีรายการมากกว่าหนึ่งการใช้งานFirst()
ทั้งสองอย่างมีประสิทธิภาพใช้รายการแรก First()
มีประสิทธิภาพมากกว่าเล็กน้อยเนื่องจากไม่ได้ตรวจสอบเพื่อดูว่ามีรายการที่สองหรือไม่
ความแตกต่างเพียงอย่างเดียวคือSingle()
คาดว่าจะมีเพียงหนึ่งรายการในการแจงนับที่จะเริ่มต้นด้วยและจะส่งข้อยกเว้นหากมีมากกว่าหนึ่งรายการ คุณใช้ .Single()
ถ้าคุณต้องการข้อยกเว้นโดยเฉพาะในกรณีนี้
ถ้าฉันจำได้ว่า Single () ตรวจสอบว่ามีองค์ประกอบอื่นหลังจากองค์ประกอบแรก (และส่งข้อยกเว้นถ้าเป็นกรณีนั้น) ในขณะที่ First () หยุดหลังจากได้รับ ทั้งโยนข้อยกเว้นถ้าลำดับว่างเปล่า
ส่วนตัวฉันใช้ First () เสมอ
เกี่ยวกับการแสดง: เพื่อนร่วมงานและฉันกำลังพูดคุยเกี่ยวกับประสิทธิภาพการทำงานของ 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 ได้เร็วขึ้นเล็กน้อยในการทดสอบของฉัน
พวกเขาแตกต่าง. ทั้งคู่ยืนยันว่าชุดผลลัพธ์ไม่ว่างเปล่า แต่ชุดเดียวยังยืนยันว่ามีผลลัพธ์ไม่เกิน 1 รายการ ฉันใช้ซิงเกิลในกรณีที่ฉันคาดหวังว่าจะมีเพียง 1 ผลลัพธ์เพราะการได้รับมากกว่า 1 ผลลัพธ์กลับมาเป็นข้อผิดพลาดและน่าจะได้รับการปฏิบัติเช่นนี้
คุณสามารถลองตัวอย่างง่ายๆเพื่อรับความแตกต่าง ข้อยกเว้นจะถูกโยนในบรรทัดที่ 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);
ผู้คนจำนวนมากที่ฉันรู้จักใช้ FirstOrDefault () แต่ฉันมักจะใช้ SingleOrDefault () มากขึ้นเพราะบ่อยครั้งที่มันจะเป็นข้อมูลที่ไม่สอดคล้องกันหากมีมากกว่าหนึ่ง นี่เป็นการจัดการกับ LINQ-to-Objects
บันทึกในนิติบุคคลพนักงาน:
Employeeid = 1
: พนักงานเพียงคนเดียวที่มี ID นี้
Firstname = Robert
: พนักงานมากกว่าหนึ่งคนที่ใช้ชื่อนี้
Employeeid = 10
: ไม่มีพนักงานที่มี ID นี้
ตอนนี้มันจำเป็นที่จะต้องเข้าใจในสิ่งที่Single()
และFirst()
รายละเอียดในรายละเอียด
เดี่ยว ()
Single () ใช้เพื่อส่งคืนเรกคอร์ดเดียวที่มีอยู่ในตารางดังนั้นแบบสอบถามด้านล่างจะส่งคืนพนักงานที่มีemployeed =1
เพราะเรามีพนักงานเพียงคนเดียวที่มีEmployeed
1 หากเรามีสองระเบียนสำหรับ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 ()ซึ่งจะจัดการกับข้อยกเว้นที่เป็นโมฆะในกรณีที่ไม่มีบันทึกพบว่ามันจะกลับมาเป็นโมฆะ