คำสั่งสืบค้นที่คอมไพล์แล้วและประสิทธิภาพ
เพิ่มประสิทธิภาพคำสั่งสืบค้นด้วย EF.CompileQuery, AsNoTracking และการฉายข้อมูลด้วย Select
คำสั่งสืบค้นที่คอมไพล์แล้วและประสิทธิภาพ เป็นบทเรียน C# Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C# Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน
การคอมไพล์คิวรีของ EF Core
ทุกครั้งที่ EF Core เรียกใช้คิวรี LINQ จะต้องแยกวิเคราะห์ต้นไม้นิพจน์ แปลงเป็น SQL และตรวจสอบความถูกต้อง สำหรับเส้นทางการทำงานที่เรียกใช้บ่อย ค่าใช้จ่ายส่วนนี้จะสะสมเพิ่มขึ้น EF Core จึงมีเครื่องมือหลายอย่างเพื่อลดค่าใช้จ่ายดังกล่าว
AsNoTracking สำหรับคิวรีแบบอ่านอย่างเดียว
AsNoTracking() จะข้ามการลงทะเบียนกับตัวติดตามการเปลี่ยนแปลง สำหรับคิวรีแบบอ่านอย่างเดียว เช่น การตอบกลับ API และรายงาน วิธีนี้เป็นการปรับปรุงที่ทำได้ง่ายและให้ผลมากที่สุด โดยทั่วไปจะเร็วขึ้น 2–5 เท่าและใช้หน่วยความจำน้อยลง
// With tracking (default) — for entities you'll modify
var tracked = await _db.Products.FirstOrDefaultAsync(p => p.Id == 1);
// Without tracking — for read-only
var dto = await _db.Products
.AsNoTracking()
.Where(p => p.IsActive)
.Select(p => new ProductDto { Id = p.Id, Name = p.Name })
.ToListAsync();คิวรีที่คอมไพล์ไว้
EF.CompileAsyncQuery จะคอมไพล์คิวรี LINQ ล่วงหน้าเป็นผู้รับมอบหมาย การแปลจะเกิดขึ้นครั้งเดียวเมื่อเริ่มต้นระบบ ส่วนการเรียกใช้ครั้งต่อ ๆ ไปจะข้ามขั้นตอนการคอมไพล์ทั้งหมด
// Define once, reuse many times
private static readonly Func<AppDbContext, int, Task<Product?>> GetProductById =
EF.CompileAsyncQuery(
(AppDbContext db, int id) =>
db.Products.AsNoTracking().FirstOrDefault(p => p.Id == id));
// Usage
var product = await GetProductById(_db, 42);การเลือกเฉพาะข้อมูลด้วย Select เพื่อประสิทธิภาพ
ควรเลือกเฉพาะฟิลด์ที่ต้องการใช้อย่างแท้จริงเสมอ การเลือกออบเจ็กต์ถ่ายโอนข้อมูลแทนเอนทิตีทั้งหมดจะลดจำนวนคอลัมน์ที่ดึง ข้อมูลที่ส่งผ่านเครือข่าย และแรงกดดันต่อ GC
// Load entire entity (over-fetch)
var all = await _db.Products.ToListAsync();
// Projection: only fetch needed columns
var names = await _db.Products
.AsNoTracking()
.Select(p => new { p.Id, p.Name, p.Price })
.ToListAsync();
// SQL: SELECT Id, Name, Price FROM Productsคำแนะนำดัชนีและการปรับคิวรีดิบให้เหมาะสม
EF Core จะใช้ดัชนีฐานข้อมูลที่คุณกำหนดค่าด้วย HasIndex() ดัชนีหลายคอลัมน์ ดัชนีแบบกรองข้อมูล และคอลัมน์ที่รวมไว้ ล้วนส่งผลต่อประสิทธิภาพของแผนการคิวรี
// Configure compound index in OnModelCreating
modelBuilder.Entity<Order>()
.HasIndex(o => new { o.CustomerId, o.Status })
.HasFilter("[Status] = 0") // filtered index
.IncludeProperties(o => o.PlacedAt);
// The query below will use this index efficiently
var pending = await _db.Orders
.AsNoTracking()
.Where(o => o.CustomerId == 5 && o.Status == OrderStatus.Pending)
.ToListAsync();การแบ่งหน้าด้วย Skip และ Take
ควรแบ่งหน้าชุดผลลัพธ์ขนาดใหญ่เสมอ Skip และ Take จะแปลเป็น SQL OFFSET/FETCH ทำให้ไม่ต้องโหลดแถวหลายพันแถวไว้ในหน่วยความจำ
int page = 2, pageSize = 25;
var products = await _db.Products
.AsNoTracking()
.Where(p => p.IsActive)
.OrderBy(p => p.Name)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
// SQL: ... ORDER BY Name OFFSET 25 ROWS FETCH NEXT 25 ROWS ONLYการนับอย่างมีประสิทธิภาพ
ใช้ CountAsync() หรือ AnyAsync() แทนการโหลดระเบียนเพียงเพื่อจะนับจำนวน AnyAsync() ยังเร็วกว่า COUNT เมื่อคุณต้องการตรวจสอบเพียงว่ามีข้อมูลอยู่หรือไม่
// Total count for pagination
int totalCount = await _db.Products
.Where(p => p.IsActive)
.CountAsync();
// Existence check — faster than Count > 0
bool hasExpensive = await _db.Products
.AnyAsync(p => p.Price > 1000m);
// SQL: IF EXISTS (SELECT 1 FROM ...) SELECT 1การแทรกข้อมูลจำนวนมากด้วย AddRange
ใช้ AddRange เพื่อเพิ่มเอนทิตีหลายรายการพร้อมกัน EF Core จะรวมคำสั่ง INSERT หลายรายการเป็นชุดเพื่อลดจำนวนการไปกลับระหว่างแอปกับฐานข้อมูล โดยควบคุมด้วย MaxBatchSize ของผู้ให้บริการ
var newProducts = Enumerable.Range(1, 1000)
.Select(i => new Product { Name = $"Product {i}", Price = i * 1.5m })
.ToList();
_db.Products.AddRange(newProducts);
await _db.SaveChangesAsync();
// Batched: typically 100-1000 rows per round-tripEF.Functions สำหรับฟังก์ชันฐานข้อมูล
EF.Functions แสดงฟังก์ชันเฉพาะของฐานข้อมูล เช่น LIKE ส่วนของวันที่ และการค้นหาข้อความเต็ม ซึ่ง EF Core จะแปลเป็น SQL แทนการประมวลผลในหน่วยความจำ
// LIKE pattern search (SQL-side)
var results = await _db.Products
.Where(p => EF.Functions.Like(p.Name, "%keyboard%"))
.AsNoTracking()
.ToListAsync();
// Date difference (SQL-side)
var recentOrders = await _db.Orders
.Where(o => EF.Functions.DateDiffDay(o.PlacedAt, DateTime.UtcNow) <= 7)
.ToListAsync();การวินิจฉัยคิวรีที่ทำงานช้า
เปิดการบันทึกคิวรีของ EF Core เพื่อดู SQL ที่สร้างขึ้น ใช้ EnableSensitiveDataLogging() ในสภาพแวดล้อมการพัฒนาเพื่อดูค่าพารามิเตอร์
builder.Services.AddDbContext<AppDbContext>(opt =>
opt.UseSqlServer(connStr)
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging() // dev only!
.EnableDetailedErrors()); // dev only!ในสถานการณ์จริง: ปลายทางรายการที่ปรับให้เหมาะสม
ปลายทางรายการที่พร้อมใช้งานจริงจะรวม AsNoTracking การเลือกเฉพาะข้อมูล ดัชนี และการแบ่งหน้าเข้าด้วยกัน เพื่อให้ได้ประสิทธิภาพสูงสุด
app.MapGet("/products", async (
AppDbContext db,
string? search,
int page = 1, int size = 20) =>
{
var query = db.Products
.AsNoTracking()
.Where(p => p.IsActive);
if (!string.IsNullOrEmpty(search))
query = query.Where(p => EF.Functions.Like(p.Name, $"%{search}%"));
var total = await query.CountAsync();
var items = await query
.OrderBy(p => p.Name)
.Skip((page - 1) * size).Take(size)
.Select(p => new { p.Id, p.Name, p.Price })
.ToListAsync();
return Results.Ok(new { total, items });
});ตรวจสอบความเข้าใจ
ประโยชน์หลักของ EF.CompileAsyncQuery เมื่อเทียบกับคิวรี LINQ ทั่วไปคืออะไร
สรุป: คิวรีที่คอมไพล์ไว้และประสิทธิภาพ
ประเด็นสำคัญ:
AsNoTracking(): ข้ามตัวติดตามการเปลี่ยนแปลงสำหรับการอ่านอย่างเดียว เป็นการปรับปรุงที่ทำได้ง่ายและเร็วที่สุดEF.CompileAsyncQuery: คอมไพล์คิวรีที่เรียกใช้บ่อยล่วงหน้าเป็นผู้รับมอบหมาย- การเลือกเฉพาะข้อมูลด้วย
Select: ดึงเฉพาะคอลัมน์ที่จำเป็น - ดัชนีที่เหมาะสม (
HasIndex) ช่วยปรับปรุงแผนการคิวรีได้อย่างมาก - แบ่งหน้าด้วย
Skip/Takeและใช้AnyAsyncเพื่อตรวจสอบว่ามีข้อมูลอยู่หรือไม่ - เปิดการบันทึกคิวรีในสภาพแวดล้อมการพัฒนาเพื่อค้นหา SQL ที่ทำงานช้าหรือไม่เป็นไปตามคาด
คำถามที่พบบ่อย
บทเรียน “คำสั่งสืบค้นที่คอมไพล์แล้วและประสิทธิภาพ” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “คำสั่งสืบค้นที่คอมไพล์แล้วและประสิทธิภาพ” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C# Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “คำสั่งสืบค้นที่คอมไพล์แล้วและประสิทธิภาพ”
เพิ่มประสิทธิภาพคำสั่งสืบค้นด้วย EF.CompileQuery, AsNoTracking และการฉายข้อมูลด้วย Select คุณปฏิบัติ C# Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C# Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน C# Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “คำสั่งสืบค้นที่คอมไพล์แล้วและประสิทธิภาพ” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน C# Academy นี้ได้ไหม
ได้ บทเรียน C# Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การโหลดแบบทันที แบบขี้เกียจ และแบบชัดแจ้ง
- SQL ดิบ โพรซีเยอร์ที่จัดเก็บ และการแทรกค่า
- คำสั่งสืบค้นที่คอมไพล์แล้วและประสิทธิภาพ
- ตัวกรองคำสั่งสืบค้นส่วนกลางและเอนทิตีที่เป็นเจ้าของ