SQL ดิบ โพรซีเยอร์ที่จัดเก็บ และการแทรกค่า
เรียกใช้ SQL ดิบอย่างปลอดภัยด้วย FromSqlRaw, FromSqlInterpolated และโพรซีเยอร์ที่จัดเก็บ พร้อมหลีกเลี่ยงการแทรกคำสั่ง
SQL ดิบ โพรซีเยอร์ที่จัดเก็บ และการแทรกค่า เป็นบทเรียน C# Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C# Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน
เมื่อใดควรใช้ SQL ดิบใน EF Core
คิวรีส่วนใหญ่ทำงานได้ดีกับ LINQ แต่บางสถานการณ์จำเป็นต้องใช้ SQL ดิบ เช่น การวิเคราะห์ที่ซับซ้อน ฟังก์ชันเฉพาะของผู้ให้บริการ โพรซีเยอร์ที่จัดเก็บไว้ หรือคิวรีที่เน้นประสิทธิภาพและ LINQ แปลได้ไม่ดี
EF Core มีวิธีที่ปลอดภัยสำหรับการใช้ SQL ดิบโดยไม่ลดทอนความปลอดภัย
FromSqlRaw: คิวรีที่ใช้พารามิเตอร์อย่างปลอดภัย
FromSqlRaw จะเรียกใช้ SQL ดิบและแมปผลลัพธ์กลับเป็นเอนทิตี ควรใช้ตัวแทน {0} เสมอ — ห้ามต่อสตริง — เพื่อป้องกันการฉีด SQL
// SAFE: parameterized
var products = await _db.Products
.FromSqlRaw("SELECT * FROM Products WHERE Price > {0}", 100m)
.ToListAsync();
// DANGEROUS: never do this
// FromSqlRaw("SELECT * FROM Products WHERE Price > " + price)FromSqlInterpolated: ความปลอดภัยของสตริงแทรกค่า
FromSqlInterpolated รับสตริงแทรกค่าของ C# โดย EF Core จะแปลงค่าที่แทรกไว้เป็นพารามิเตอร์ SQL โดยอัตโนมัติ จึงปลอดภัยและอ่านเข้าใจง่าย
decimal minPrice = 100m;
string category = "Electronics";
var products = await _db.Products
.FromSqlInterpolated(
$"SELECT * FROM Products WHERE Price > {minPrice} AND Category = {category}")
.OrderBy(p => p.Name)
.ToListAsync();
// Values become @p0, @p1 parameters automaticallyการประกอบคิวรี LINQ หลัง FromSql
คุณสามารถเรียกตัวดำเนินการ LINQ ต่อจาก FromSql ได้ EF Core จะครอบ SQL ดิบของคุณไว้เป็นคิวรีย่อย แล้วนำ LINQ ไปใช้ต่อด้านบน ซึ่งมีประโยชน์สำหรับการเพิ่มการกรองหรือการแบ่งหน้า
decimal min = 50m;
var page = await _db.Products
.FromSqlInterpolated($"SELECT * FROM Products WHERE Price > {min}")
.Where(p => p.IsActive) // additional LINQ filter
.OrderBy(p => p.Price)
.Skip(0).Take(20) // pagination
.ToListAsync();
// Generates: SELECT ... FROM (raw SQL) AS p WHERE ...การเรียกใช้โพรซีเยอร์ที่จัดเก็บไว้
เรียกโพรซีเยอร์ที่จัดเก็บไว้ซึ่งส่งคืนแถวของเอนทิตีผ่าน FromSqlRaw สำหรับโพรซีเยอร์ที่ไม่ส่งคืนแถว เช่น INSERT/UPDATE ให้ใช้ ExecuteSqlRawAsync
// Stored proc returning Products
var products = await _db.Products
.FromSqlRaw("EXEC GetProductsByCategory {0}", "Books")
.ToListAsync();
// Stored proc with no return rows
await _db.Database.ExecuteSqlRawAsync(
"EXEC ArchiveOldOrders {0}",
DateTime.UtcNow.AddYears(-1));ExecuteSqlInterpolated สำหรับ DML
ExecuteSqlInterpolated เรียกใช้คำสั่ง DML (INSERT, UPDATE, DELETE) พร้อมความปลอดภัยของสตริงแทรกค่า โดยจะส่งคืนจำนวนแถวที่ได้รับผลกระทบ
int days = 30;
int affected = await _db.Database.ExecuteSqlInterpolatedAsync(
$"UPDATE Orders SET Status = 'Expired' WHERE PlacedAt < {DateTime.UtcNow.AddDays(-days)}");
Console.WriteLine($"{affected} orders expired");การคิวรีชนิดที่ไม่ใช่เอนทิตี
ใช้ SqlQuery<T> (.NET 7+) เพื่อคิวรีชนิดสเกลาร์หรือออบเจ็กต์ถ่ายโอนข้อมูลแบบใดก็ได้ที่ไม่ใช่เอนทิตีของ EF Core โดยไม่จำเป็นต้องมี DbSet
// Query a DTO directly — no entity needed
var stats = await _db.Database
.SqlQuery<ProductStats>(
$"SELECT Category, COUNT(*) AS Count, AVG(Price) AS AvgPrice FROM Products GROUP BY Category")
.ToListAsync();
public record ProductStats(string Category, int Count, decimal AvgPrice);ชนิดเอนทิตีที่ไม่มีคีย์สำหรับมุมมอง
แมปมุมมองฐานข้อมูลหรือผลลัพธ์จากคิวรีดิบไปยัง ชนิดเอนทิตีที่ไม่มีคีย์ โดยใช้ HasNoKey() และ ToView() จากนั้นคิวรีได้เหมือนกับ DbSet โดยไม่ต้องมีคีย์หลัก
public class ProductSalesSummary
{
public string Category { get; set; } = "";
public int TotalSold { get; set; }
public decimal Revenue { get; set; }
}
// In OnModelCreating:
modelBuilder.Entity<ProductSalesSummary>()
.HasNoKey()
.ToView("vw_ProductSalesSummary");
// Query:
var summary = await _db.Set<ProductSalesSummary>().ToListAsync();หลีกเลี่ยงข้อผิดพลาดจากการฉีด SQL
กฎสำคัญที่สุดคือ ห้ามต่ออินพุตจากผู้ใช้เข้ากับสตริง SQL โดยตรง ควรใช้คิวรีที่มีพารามิเตอร์เสมอผ่านตัวแทนของ FromSqlRaw, FromSqlInterpolated หรือ LINQ ของ EF Core
// SAFE
var name = userInput;
var result = await _db.Products
.FromSqlInterpolated($"SELECT * FROM Products WHERE Name = {name}")
.ToListAsync();
// INJECTION RISK (never do this)
// FromSqlRaw("SELECT * FROM Products WHERE Name = '" + name + "'");พารามิเตอร์ผลลัพธ์พร้อมทางเลือกสำรอง ADO.NET
EF Core ไม่รองรับพารามิเตอร์ผลลัพธ์ของโพรซีเยอร์ที่จัดเก็บไว้โดยตรง สำหรับกรณีเหล่านี้ให้เปลี่ยนไปใช้ ADO.NET ผ่าน _db.Database.GetDbConnection()
await _db.Database.OpenConnectionAsync();
var conn = _db.Database.GetDbConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "EXEC GetOrderCount @CustomerId, @Count OUTPUT";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("@CustomerId", 5));
var outParam = new SqlParameter("@Count", SqlDbType.Int) { Direction = ParameterDirection.Output };
cmd.Parameters.Add(outParam);
await cmd.ExecuteNonQueryAsync();
int count = (int)outParam.Value;ในสถานการณ์จริง: ทางเลือกสำรองสำหรับการค้นหาข้อความเต็ม
LINQ ไม่สามารถแสดงเงื่อนไขการค้นหาข้อความเต็มที่เฉพาะกับฐานข้อมูลได้ ให้ใช้ FromSqlInterpolated เพื่อใช้ความสามารถ FREETEXT หรือ tsvector ดั้งเดิม
string query = "keyboard mechanical";
var results = await _db.Products
.FromSqlInterpolated(
$"SELECT * FROM Products WHERE FREETEXT(Name, {query})")
.AsNoTracking()
.Take(20)
.ToListAsync();ตรวจสอบความเข้าใจ
เหตุใด FromSqlInterpolated จึงปลอดภัยกว่าการต่อสตริงสำหรับคิวรีที่ใช้พารามิเตอร์
สรุป: SQL ดิบใน EF Core
ประเด็นสำคัญ:
FromSqlRaw: ใช้ตัวแทน {0} และห้ามต่ออินพุตจากผู้ใช้เข้ากับสตริงFromSqlInterpolated: สตริงแทรกค่าที่ปลอดภัย → พารามิเตอร์ ADO.NET- เรียกตัวดำเนินการ LINQ ต่อจาก FromSql เพื่อเพิ่มการกรอง การเรียงลำดับ และการแบ่งหน้า
ExecuteSqlInterpolatedAsync: การเรียกใช้ DML อย่างปลอดภัยSqlQuery<T>: คิวรีออบเจ็กต์ถ่ายโอนข้อมูลที่ไม่ใช่เอนทิตี (.NET 7+)- ใช้ ADO.NET โดยตรงสำหรับพารามิเตอร์ผลลัพธ์หรือโพรซีเยอร์ที่จัดเก็บไว้ซับซ้อน
คำถามที่พบบ่อย
บทเรียน “SQL ดิบ โพรซีเยอร์ที่จัดเก็บ และการแทรกค่า” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “SQL ดิบ โพรซีเยอร์ที่จัดเก็บ และการแทรกค่า” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C# Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “SQL ดิบ โพรซีเยอร์ที่จัดเก็บ และการแทรกค่า”
เรียกใช้ SQL ดิบอย่างปลอดภัยด้วย FromSqlRaw, FromSqlInterpolated และโพรซีเยอร์ที่จัดเก็บ พร้อมหลีกเลี่ยงการแทรกคำสั่ง คุณปฏิบัติ C# Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C# Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน C# Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “SQL ดิบ โพรซีเยอร์ที่จัดเก็บ และการแทรกค่า” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน C# Academy นี้ได้ไหม
ได้ บทเรียน C# Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การโหลดแบบทันที แบบขี้เกียจ และแบบชัดแจ้ง
- SQL ดิบ โพรซีเยอร์ที่จัดเก็บ และการแทรกค่า
- คำสั่งสืบค้นที่คอมไพล์แล้วและประสิทธิภาพ
- ตัวกรองคำสั่งสืบค้นส่วนกลางและเอนทิตีที่เป็นเจ้าของ