แบบสอบถาม Entity Framework ช้า แต่ SQL เดียวกันใน SqlQuery นั้นเร็ว


95

ฉันเห็นความสมบูรณ์แบบแปลก ๆ ที่เกี่ยวข้องกับแบบสอบถามที่เรียบง่ายมากโดยใช้ Entity Framework Code-First กับ. NET framework เวอร์ชัน 4 แบบสอบถาม LINQ2Entities มีลักษณะดังนี้:

 context.MyTables.Where(m => m.SomeStringProp == stringVar);

ซึ่งใช้เวลามากกว่า 3,000 มิลลิวินาทีในการดำเนินการ SQL ที่สร้างขึ้นนั้นดูเรียบง่ายมาก:

 SELECT [Extent1].[ID], [Extent1].[SomeStringProp], [Extent1].[SomeOtherProp],
 ...
 FROM [MyTable] as [Extent1]
 WHERE [Extent1].[SomeStringProp] = '1234567890'

แบบสอบถามนี้ทำงานเกือบจะทันทีเมื่อเรียกใช้ผ่าน Management Studio เมื่อฉันเปลี่ยนรหัส C # เพื่อใช้ฟังก์ชัน SqlQuery มันจะทำงานใน 5-10 มิลลิวินาที:

 context.MyTables.SqlQuery("SELECT [Extent1].[ID] ... WHERE [Extent1].[SomeStringProp] = @param", stringVar);

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


2
ฉันคาดว่าคุณจะเห็นความล่าช้าในการเริ่มต้น - อาจดูการรวบรวม ดู MSDN:Performance Considerations for Entity Framework 5
Nicholas Butler

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

1
@marc_s - ไม่ SqlQuery จะส่งคืนอินสแตนซ์เอนทิตีที่เป็นจริงและติดตามการเปลี่ยนแปลง ดูmsdn.microsoft.com/en-us/library/…
Brian Sullivan

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

คุณลองเรียกใช้แบบสอบถามเดียวกันสองครั้ง / หลายครั้งหรือไม่? เมื่อวิ่งครั้งที่สองใช้เวลานานแค่ไหน? คุณเคยลองสิ่งนี้บน. NET Framework 4.5 หรือไม่ - มีการปรับปรุงประสิทธิภาพที่เกี่ยวข้องกับ EF ใน. NET Framework 4.5 ที่สามารถช่วยได้
Pawel

คำตอบ:


97

พบแล้ว ปรากฎว่าเป็นปัญหาของประเภทข้อมูล SQL SomeStringPropคอลัมน์ในฐานข้อมูลเป็น varchar แต่ EF อนุมานว่า .NET ประเภทสตริงเป็น nvarchars กระบวนการแปลที่ได้ในระหว่างการสืบค้นเพื่อให้ DB ทำการเปรียบเทียบเป็นสิ่งที่ใช้เวลานาน ฉันคิดว่า EF Prof กำลังทำให้ฉันหลงทางอยู่ที่นี่การแสดงคำค้นหาที่ถูกต้องมากขึ้นจะเป็นดังต่อไปนี้:

 SELECT [Extent1].[ID], [Extent1].[SomeStringProp], [Extent1].[SomeOtherProp],
 ...
 FROM [MyTable] as [Extent1]
 WHERE [Extent1].[SomeStringProp] = N'1234567890'

ดังนั้นการแก้ไขที่ได้คือการใส่คำอธิบายประกอบโมเดลรหัสแรกซึ่งระบุชนิดข้อมูล SQL ที่ถูกต้อง:

public class MyTable
{
    ...

    [Column(TypeName="varchar")]
    public string SomeStringProp { get; set; }

    ...
}

1
การสอบสวนที่ดี คำถามของคุณได้รับความทุกข์ทรมานจาก "การแปลงโดยนัย" ตามที่อธิบายไว้ที่นี่: brentozar.com/archive/2012/07/…
ไจ

ช่วยฉันไม่กี่ชั่วโมงในการแก้ไขข้อบกพร่อง นี่คือปัญหา
Cody

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

1
ผู้ชายที่ยอดเยี่ยม แต่ @Jaime เราควรทำอย่างไรสำหรับแนวทางแรกของฐานข้อมูลเนื่องจากทุกอย่าง (เช่นคำอธิบายประกอบข้อมูลในโมเดลฐานข้อมูล) จะถูกลบออกไปหลังจากอัปเดตโมเดล EF จากฐานข้อมูล
Nauman Khan

การตั้งค่านี้เป็นโฮมเพจของฉันเพื่อให้ฉันได้หวนระลึกถึงความตื่นเต้นในการค้นหาคำตอบที่ยอดเยี่ยมอีกครั้ง ขอบคุณ!!!
OJisBad

44

เหตุผลที่ทำให้การสืบค้นใน EF ช้าลงคือการเปรียบเทียบสเกลาร์ที่ไม่เป็นโมฆะกับสเกลาร์ที่เป็นโมฆะ:

long? userId = 10; // nullable scalar

db.Table<Document>().Where(x => x.User.Id == userId).ToList() // or userId.Value
                                ^^^^^^^^^    ^^^^^^
                                Type: long   Type: long?

ข้อความค้นหานั้นใช้เวลา 35 วินาที แต่การปรับโครงสร้างเล็ก ๆ เช่นนั้น:

long? userId = 10;
long userIdValue = userId.Value; // I've done that only for the presentation pursposes

db.Table<Document>().Where(x => x.User.Id == userIdValue).ToList()
                                ^^^^^^^^^    ^^^^^^^^^^^
                                Type: long   Type: long

ให้ผลลัพธ์ที่เหลือเชื่อ ใช้เวลาเพียง 50 มิลลิวินาทีในการทำให้เสร็จสมบูรณ์ เป็นไปได้ว่าเป็นบั๊กใน EF


13
นี่มันแปลกมาก
Daniel Cardenas

1
พระเจ้าช่วย. สิ่งนี้สามารถเกิดขึ้นได้เช่นกันเมื่อใช้อินเทอร์เฟซ IUserId.Id ทำให้เกิดปัญหากับฉัน แต่การแมป Id ครั้งแรกกับจำนวนเต็มใช้งานได้ ... ฉันต้องตรวจสอบข้อความค้นหาทั้งหมดในแอปพลิเคชัน 100.000 บรรทัดของฉันตอนนี้หรือไม่
Dirk Boer

มีการรายงานข้อบกพร่องนี้หรือไม่ ยังคงเป็นเวอร์ชันล่าสุด 6.2.0
Dirk Boer

2
ปัญหาเดียวกันนี้ยังอยู่ใน EF Core ขอบคุณที่ค้นหาสิ่งนี้!
Yannickv

ข้อเสนอแนะอื่น ๆ คือการประมวลผลตัวแปรก่อนที่จะใส่ลงในนิพจน์ LINQ มิฉะนั้น sql ที่สร้างขึ้นจะยาวและช้าลงมาก ฉันพบเมื่อมี Trim () และ ToLower () ในนิพจน์ LINQ ที่ทำให้ฉันยุ่ง
samheihey


4

ฉันมีปัญหาเดียวกัน (การสืบค้นรวดเร็วเมื่อดำเนินการจากตัวจัดการ SQL) แต่เมื่อดำเนินการจาก EF ระยะหมดเวลาจะหมดอายุ

ปรากฎว่าเอนทิตี (ซึ่งสร้างขึ้นจากมุมมอง) มีคีย์เอนทิตีไม่ถูกต้อง ดังนั้นเอนทิตีจึงมีแถวที่ซ้ำกันโดยมีคีย์เดียวกันและฉันเดาว่ามันต้องทำการจัดกลุ่มบนพื้นหลัง


3

ฉันเจอสิ่งนี้ด้วยแบบสอบถาม ef ที่ซับซ้อน การแก้ไขอย่างหนึ่งสำหรับฉันซึ่งลดแบบสอบถาม ef 6 วินาทีลงในแบบสอบถามย่อยที่สองที่สร้างขึ้นคือการปิดการโหลดแบบขี้เกียจ

หากต้องการค้นหาการตั้งค่านี้ (ef 6) ให้ไปที่ไฟล์. edmx และดูใน Properties -> Code generation -> Lazy Loading Enabled ตั้งค่าเป็นเท็จ

การปรับปรุงประสิทธิภาพครั้งใหญ่สำหรับฉัน


4
เยี่ยมมาก แต่ไม่มีส่วนเกี่ยวข้องกับคำถามของผู้โพสต์
Jace Rhea

2

ฉันมีปัญหานี้เช่นกัน แต่กลับกลายเป็นผู้ร้ายในกรณีของฉันเป็น SQL เซิร์ฟเวอร์ดมพารามิเตอร์

เบาะแสแรกที่ปัญหาของฉันเกิดจากการดมพารามิเตอร์คือการเรียกใช้แบบสอบถามด้วย "set arithabort off" หรือ "set arithabort on" ทำให้เวลาในการดำเนินการแตกต่างกันอย่างมากใน Management Studio เนื่องจาก ADO.NET โดยค่าเริ่มต้นใช้ "set arithabort off" และ Management Studio มีค่าเริ่มต้นเป็น "set arithabort on" แคชแผนแบบสอบถามจะเก็บแผนต่างๆไว้โดยขึ้นอยู่กับพารามิเตอร์นี้

ฉันแคชแผนแบบสอบถามปิดใช้งานสำหรับการค้นหาด้วยวิธีที่คุณสามารถหาได้ที่นี่

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