การโหลดแบบทันที แบบขี้เกียจ และแบบชัดแจ้ง
ควบคุมเวลาที่ข้อมูลที่เกี่ยวข้องถูกโหลดด้วย Include, ThenInclude, พร็อกซีสำหรับการโหลดแบบขี้เกียจ และการเรียก Load อย่างชัดแจ้ง
การโหลดแบบทันที แบบขี้เกียจ และแบบชัดแจ้ง เป็นบทเรียน C# Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C# Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน
กลยุทธ์การโหลดสามแบบ
EF Core มีกลยุทธ์สามแบบสำหรับโหลดข้อมูลที่เกี่ยวข้อง ได้แก่ การโหลดล่วงหน้า (โหลดพร้อมกับการสอบถาม) การโหลดเมื่อเรียกใช้ (โหลดเมื่อมีการเข้าถึง) และ การโหลดอย่างชัดแจ้ง (โหลดเมื่อต้องการ) การเลือกวิธีที่เหมาะสมช่วยหลีกเลี่ยงปัญหา N+1 และการส่งคำขอไปกลับฐานข้อมูลที่ไม่จำเป็น
การโหลดล่วงหน้าด้วย Include
การโหลดล่วงหน้าใช้ Include() เพื่อ JOIN ข้อมูลที่เกี่ยวข้องในคำสั่งสอบถาม SQL เดียวกัน วิธีนี้คาดการณ์ผลลัพธ์ได้ง่ายที่สุดและช่วยหลีกเลี่ยงปัญหา N+1
// Single query with JOIN
var blogs = await _db.Blogs
.Include(b => b.Posts)
.Where(b => b.IsActive)
.ToListAsync();
// SQL: SELECT * FROM Blogs LEFT JOIN Posts ON ...ThenInclude สำหรับความสัมพันธ์แบบซ้อนกัน
ThenInclude จะเชื่อมต่อการโหลดให้ลึกลงไปในกราฟการนำทาง คุณสามารถโหลดข้อมูลหลายระดับได้ในการสอบถามเดียว
var orders = await _db.Orders
.Include(o => o.Customer)
.Include(o => o.Lines)
.ThenInclude(l => l.Product)
.ThenInclude(p => p.Category)
.Where(o => o.Status == OrderStatus.Pending)
.ToListAsync();Include แบบกรองข้อมูล
EF Core 5+ รองรับการกรองข้อมูลที่เกี่ยวข้องภายใน Include ทำให้คุณโหลดเอนทิตีลูกเพียงบางส่วนได้โดยไม่ต้องใช้การสอบถามแยกต่างหาก
// Load only published posts for each blog
var blogs = await _db.Blogs
.Include(b => b.Posts.Where(p => p.IsPublished)
.OrderByDescending(p => p.PublishedAt)
.Take(5))
.ToListAsync();การตั้งค่าการโหลดเมื่อเรียกใช้
การโหลดเมื่อเรียกใช้จะโหลดพร็อพเพอร์ตีการนำทางโดยอัตโนมัติในครั้งแรกที่มีการเข้าถึง เปิดใช้โดยติดตั้งแพ็กเกจ Microsoft.EntityFrameworkCore.Proxies และเรียกใช้ UseLazyLoadingProxies()
// 1. Install package: dotnet add package Microsoft.EntityFrameworkCore.Proxies
// 2. Enable in DbContext setup:
builder.Services.AddDbContext<AppDbContext>(opt =>
opt.UseSqlite(connStr)
.UseLazyLoadingProxies());
// 3. Mark navigation properties as virtual:
public class Blog
{
public int Id { get; set; }
public virtual ICollection<Post> Posts { get; set; } = new List<Post>();
}การโหลดเมื่อเรียกใช้จริง (และกับดัก N+1)
การโหลดเมื่อเรียกใช้สะดวก แต่เป็นอันตรายเมื่อใช้ในลูป เพราะการเข้าถึงแต่ละครั้งจะส่งคำสั่งสอบถาม SQL ใหม่ ทำให้เกิดปัญหา N+1 ในกรณีที่แสดงรายการ ควรเลือกใช้การโหลดล่วงหน้าเสมอ
var blogs = await _db.Blogs.ToListAsync(); // 1 query
foreach (var blog in blogs)
{
// BAD: each access fires a new query!
var count = blog.Posts.Count; // N queries
Console.WriteLine(count);
}
// FIX: Use eager loading
var blogs2 = await _db.Blogs.Include(b => b.Posts).ToListAsync();การโหลดอย่างชัดแจ้ง
การโหลดอย่างชัดแจ้งช่วยให้คุณโหลดพร็อพเพอร์ตีการนำทางเมื่อต้องการ โดยใช้ Entry().Collection().LoadAsync() หรือ Entry().Reference().LoadAsync() เหมาะสำหรับกรณีที่คุณต้องการข้อมูลที่เกี่ยวข้องเป็นบางเงื่อนไข
var blog = await _db.Blogs.FindAsync(1); // no posts loaded
// Load posts only when needed
if (needsPosts)
{
await _db.Entry(blog!)
.Collection(b => b.Posts)
.LoadAsync();
}
// Load single reference
var post = await _db.Posts.FindAsync(99);
await _db.Entry(post!)
.Reference(p => p.Blog)
.LoadAsync();การโหลดอย่างชัดแจ้งพร้อมตัวกรองการสอบถาม
คุณสามารถกรองการโหลดอย่างชัดแจ้งได้โดยใช้ .Query() ก่อนเรียกใช้ LoadAsync() เพื่อลดปริมาณข้อมูลที่ส่งกลับ
var blog = await _db.Blogs.FindAsync(1);
// Only load posts published this year
await _db.Entry(blog!)
.Collection(b => b.Posts)
.Query()
.Where(p => p.PublishedAt.Year == DateTime.UtcNow.Year)
.LoadAsync();
Console.WriteLine(blog!.Posts.Count);การสอบถามแบบแบ่งส่วนสำหรับ Include จำนวนมาก
การโหลดคอลเลกชันหลายรายการในคิวรีเดียวทำให้เกิดการเพิ่มจำนวนแบบคาร์ทีเซียน (แถวจะถูกคูณเพิ่มขึ้น) ใช้ AsSplitQuery() เพื่อส่งคิวรีแยกกันสำหรับแต่ละคอลเลกชัน แล้วนำมารวมกันในหน่วยความจำ
var orders = await _db.Orders
.Include(o => o.Lines)
.Include(o => o.Shipments)
.AsSplitQuery() // 3 separate SELECTs instead of one giant JOIN
.ToListAsync();
// Or configure globally:
opt.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);การเลือกเฉพาะข้อมูลเพื่อหลีกเลี่ยงการดึงข้อมูลเกินจำเป็น
แทนที่จะโหลดเอนทิตีทั้งหมดพร้อมความสัมพันธ์ทั้งหมด ให้เลือกเฉพาะข้อมูลที่ต้องการไปยังออบเจ็กต์ถ่ายโอนข้อมูลด้วย Select วิธีนี้จะได้ข้อมูลตรงตามที่ต้องการในคิวรีเดียวที่ปรับให้เหมาะสมแล้ว
var summaries = await _db.Blogs
.Select(b => new BlogSummaryDto
{
Name = b.Name,
PostCount = b.Posts.Count(p => p.IsPublished),
LatestPost = b.Posts
.Where(p => p.IsPublished)
.OrderByDescending(p => p.PublishedAt)
.Select(p => p.Title)
.FirstOrDefault()
})
.ToListAsync();
// No Include needed - translated to subqueriesในสถานการณ์จริง: การเลือกกลยุทธ์ที่เหมาะสม
นำแนวทางตัดสินใจนี้ไปใช้ได้จริง:
- การโหลดล่วงหน้า + Include: ต้องใช้ข้อมูลที่เกี่ยวข้องเสมอ เช่น หน้ารายการ
- การเลือกเฉพาะข้อมูล: มุมมองแบบอ่านอย่างเดียวและการตอบกลับ API — มีประสิทธิภาพสูงสุด
- การโหลดแบบระบุชัด: โหลดตามเงื่อนไขที่กำหนดโดยตรรกะของแอป
- การโหลดแบบเลื่อนเวลา: ควรหลีกเลี่ยงในแอปเว็บ ใช้เฉพาะกับเดสก์ท็อปหรือสคริปต์ที่มีชุดข้อมูลขนาดเล็ก
ตรวจสอบความเข้าใจ
ปัญหา N+1 ในบริบทของการโหลดแบบเลื่อนเวลาคืออะไร
สรุป: กลยุทธ์การโหลด
ประเด็นสำคัญ:
- การโหลดล่วงหน้า (
Include): โหลดข้อมูลในคิวรีเดียว เหมาะที่สุดสำหรับรูปแบบการเข้าถึงที่คาดการณ์ได้ - Include แบบกรองข้อมูล: โหลดเฉพาะข้อมูลลูกค้าบางส่วน — รองรับตั้งแต่ EF Core 5+
- การโหลดแบบเลื่อนเวลา: สะดวกแต่มีความเสี่ยง — หลีกเลี่ยงการใช้ในลูป (ปัญหา N+1)
- การโหลดแบบระบุชัด: โหลดเมื่อจำเป็นและควบคุมได้ละเอียด
- การเลือกเฉพาะข้อมูลด้วย
Select: มีประสิทธิภาพสูงสุดสำหรับกรณีอ่านอย่างเดียว - คิวรีแบบแยกส่วน: หลีกเลี่ยงการเพิ่มจำนวนแบบคาร์ทีเซียนเมื่อใช้ Include กับคอลเลกชันหลายรายการ
คำถามที่พบบ่อย
บทเรียน “การโหลดแบบทันที แบบขี้เกียจ และแบบชัดแจ้ง” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การโหลดแบบทันที แบบขี้เกียจ และแบบชัดแจ้ง” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C# Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การโหลดแบบทันที แบบขี้เกียจ และแบบชัดแจ้ง”
ควบคุมเวลาที่ข้อมูลที่เกี่ยวข้องถูกโหลดด้วย Include, ThenInclude, พร็อกซีสำหรับการโหลดแบบขี้เกียจ และการเรียก Load อย่างชัดแจ้ง คุณปฏิบัติ C# Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C# Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน C# Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน
บทเรียน “การโหลดแบบทันที แบบขี้เกียจ และแบบชัดแจ้ง” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน C# Academy นี้ได้ไหม
ได้ บทเรียน C# Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การโหลดแบบทันที แบบขี้เกียจ และแบบชัดแจ้ง
- SQL ดิบ โพรซีเยอร์ที่จัดเก็บ และการแทรกค่า
- คำสั่งสืบค้นที่คอมไพล์แล้วและประสิทธิภาพ
- ตัวกรองคำสั่งสืบค้นส่วนกลางและเอนทิตีที่เป็นเจ้าของ