C # Entity Framework: ปัญหาหน่วยความจำอินพุตส่วนขยายจำนวนมาก
ฉันกำลังใช้ EF Extensions สิ่งหนึ่งที่ฉันไม่เข้าใจ "มันควรจะช่วยในการแสดง"
อย่างไรก็ตามการวางล้าน + บันทึกลงในตัวแปรรายการเป็นปัญหาหน่วยความจำเอง ดังนั้นหากต้องการอัปเดตข้อมูลนับล้านรายการโดยไม่เก็บทุกอย่างไว้ในหน่วยความจำจะทำได้อย่างไร?
เราควรใช้ a for loop
และการอัปเดตเป็นชุด ๆ บอกว่า 10,000? EFExtensions BulkUpdate มีฟังก์ชันดั้งเดิมที่รองรับสิ่งนี้หรือไม่
ตัวอย่าง:
var productUpdate = _dbContext.Set<Product>()
.Where(x => x.ProductType == 'Electronics'); // this creates IQueryable
await productUpdate.ForEachAsync(c => c.ProductBrand = 'ABC Company');
_dbContext.BulkUpdateAsync(productUpdate.ToList());
ทรัพยากร:
https://entityframework-extensions.net/bulk-update
คำตอบ
ฉันพบวิธีส่วนขยาย EF ที่ "เหมาะสม" ในการอัปเดตจำนวนมากโดยมีเงื่อนไขคล้ายคำค้นหา:
var productUpdate = _dbContext.Set<Product>()
.Where(x => x.ProductType == 'Electronics')
.UpdateFromQuery( x => new Product { ProductBrand = "ABC Company" });
สิ่งนี้ควรทำให้เกิด SQL ที่เหมาะสมUPDATE ... SET ... WHERE
โดยไม่จำเป็นต้องโหลดเอนทิตีก่อนตามเอกสาร :
ทำไม
UpdateFromQuery
จะเร็วกว่าSaveChanges
,BulkSaveChanges
และBulkUpdate
?
UpdateFromQuery
ดำเนินการคำสั่งโดยตรงใน SQL เช่นUPDATE [TableName] SET [SetColumnsAndValues] WHERE [Key]
.การดำเนินการอื่น ๆ โดยปกติต้องใช้ฐานข้อมูลแบบไปกลับหนึ่งหรือหลายรอบซึ่งทำให้ประสิทธิภาพช้าลง
คุณสามารถตรวจสอบไวยากรณ์ที่ใช้งานได้ในตัวอย่างดอทเน็ตซอซึ่งดัดแปลงมาจากตัวอย่างของBulkUpdate
.
ข้อควรพิจารณาอื่น ๆ
ไม่มีการกล่าวถึงการดำเนินการแบบกลุ่มสำหรับสิ่งนี้น่าเสียดาย
ก่อนที่จะทำการอัปเดตครั้งใหญ่เช่นนี้คุณควรพิจารณาปิดใช้งานดัชนีที่คุณอาจมีในคอลัมน์นี้และสร้างขึ้นใหม่ในภายหลัง สิ่งนี้มีประโยชน์อย่างยิ่งหากคุณมีหลายคน
ระมัดระวังเกี่ยวกับเงื่อนไขใน
Where
หากไม่สามารถแปลเป็น SQL โดย EF ได้ก็จะทำในฝั่งไคลเอ็นต์ซึ่งหมายถึงการโหลดแบบไปกลับแย่มาก "ปกติ" - เปลี่ยนหน่วยความจำ - อัปเดต "
นี่คือสิ่งที่ EF ไม่ได้สร้างมาเพื่อ การโต้ตอบฐานข้อมูลของ EF เริ่มต้นจากวัตถุบันทึกและไหลจากที่นั่น EF ไม่สามารถสร้างการอัปเดตบางส่วนได้ (กล่าวคือไม่เขียนทับทุกอย่าง) หากเอนทิตีไม่ได้ติดตามการเปลี่ยนแปลง (และโหลดแล้ว) และในทำนองเดียวกันจะไม่สามารถลบบันทึกตามเงื่อนไขแทนคีย์ได้
ไม่มี EF เทียบเท่า (โดยไม่ต้องโหลดบันทึกทั้งหมด) สำหรับตรรกะการอัพเดต / ลบแบบมีเงื่อนไขเช่น
UPDATE People
SET FirstName = 'Bob'
WHERE FirstName = 'Robert'
หรือ
DELETE FROM People
WHERE FirstName = 'Robert'
การทำเช่นนี้โดยใช้แนวทาง EF จะทำให้คุณต้องโหลดเอนทิตีเหล่านี้ทั้งหมดเพียงเพื่อส่งกลับ (ด้วยการอัปเดตหรือลบ) ไปยังฐานข้อมูลและนั่นเป็นการสิ้นเปลืองแบนด์วิดท์และประสิทธิภาพอย่างที่คุณพบแล้ว
ทางออกที่ดีที่สุดที่ฉันพบที่นี่คือการหลีกเลี่ยงวิธีการที่เป็นมิตรกับ LINQ ของ EF และดำเนินการ SQL ดิบด้วยตัวคุณเองแทน สามารถทำได้โดยใช้บริบท EF
using (var ctx = new MyContext())
{
string updateCommand = "UPDATE People SET FirstName = 'Bob' WHERE FirstName = 'Robert'";
int noOfRowsUpdated = ctx.Database.ExecuteSqlCommand(updateCommand);
string deleteCommand = "DELETE FROM People WHERE FirstName = 'Robert'";
int noOfRowsDeleted = ctx.Database.ExecuteSqlCommand(deleteCommand);
}
ข้อมูลเพิ่มเติมที่นี่ แน่นอนอย่าลืมป้องกันการฉีด SQLที่เกี่ยวข้อง
ไวยากรณ์เฉพาะสำหรับเรียกใช้ SQL ดิบอาจแตกต่างกันไปตามรุ่นของ EF / EF Core แต่เท่าที่ฉันทราบทุกเวอร์ชันอนุญาตให้คุณเรียกใช้ SQL ดิบได้
ฉันไม่สามารถแสดงความคิดเห็นเกี่ยวกับประสิทธิภาพของ EF Extensions หรือ BulkUpdate โดยเฉพาะได้และฉันจะไม่ซื้อจากพวกเขา
จากเอกสารของพวกเขาดูเหมือนว่าพวกเขาจะไม่มีวิธีการที่มีลายเซ็นที่ถูกต้องเพื่อให้ตรรกะการอัปเดต / ลบตามเงื่อนไข
BulkUpdate
ดูเหมือนจะไม่อนุญาตให้คุณป้อนเงื่อนไขทางตรรกะ (WHERE ในคำสั่ง UPDATE ของคุณ) ที่จะอนุญาตให้คุณปรับแต่งสิ่งนี้ให้เหมาะสมBulkDelete
ยังคงมีการBatchSize
ตั้งค่าซึ่งแสดงให้เห็นว่าพวกเขายังคงจัดการระเบียนทีละรายการ (ฉันเดาว่าต่อชุด) และไม่ได้ใช้แบบสอบถาม DELETE เดียวที่มีเงื่อนไข (ส่วนคำสั่ง WHERE)
จากรหัสตั้งใจของคุณในคำถาม EF Extensions ไม่ได้ให้สิ่งที่คุณต้องการจริงๆ ประสิทธิภาพมากกว่าและราคาถูกกว่าในการเรียกใช้ SQL ดิบบนฐานข้อมูลเนื่องจากสิ่งนี้ข้ามความต้องการของ EF ในการโหลดเอนทิตี
ปรับปรุง
ฉันอาจยืนแก้ไขมีบางการสนับสนุนสำหรับตรรกะการปรับปรุงเงื่อนไขเท่าที่เห็นนี่ อย่างไรก็ตามมันไม่ชัดเจนสำหรับฉันในขณะที่ตัวอย่างยังคงโหลดทุกอย่างในหน่วยความจำและจุดประสงค์ของตรรกะที่มีเงื่อนไขนั้นคืออะไรถ้าคุณโหลดทั้งหมดในหน่วยความจำแล้ว (ทำไมไม่ใช้ LINQ ในหน่วยความจำล่ะ)
อย่างไรก็ตามแม้ว่าจะใช้งานได้โดยไม่ต้องโหลดเอนทิตี แต่ก็ยัง:
- จำกัด มากขึ้น (อนุญาตให้ตรวจสอบความเท่าเทียมกันเท่านั้นเทียบกับ SQL ที่อนุญาตเงื่อนไขบูลีนที่เป็น SQL ที่ถูกต้อง)
- ค่อนข้างซับซ้อน (ฉันไม่ชอบไวยากรณ์ของพวกเขาบางทีนั่นอาจเป็นเรื่องส่วนตัว)
- และมีราคาแพงกว่า (ยังคงเป็นห้องสมุดแบบเสียค่าใช้จ่าย)
เทียบกับการเรียกใช้แบบสอบถาม SQL ดิบของคุณเอง ฉันยังคงแนะนำให้ใช้ SQL ดิบของคุณเองที่นี่ แต่นั่นเป็นเพียงความคิดเห็นของฉัน