Raw SQL、ストアドプロシージャと補間
FromSqlRaw、FromSqlInterpolated、ストアドプロシージャでRaw SQLを安全に実行し、インジェクションを防ぎます。
「Raw SQL、ストアドプロシージャと補間」はCoddyKit上の無料C# Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC# Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C# Academyコースには全4レッスンが含まれています。
EF CoreでRaw SQLを使うタイミング
ほとんどのクエリはLINQで問題なく処理できますが、複雑な分析、ベンダー固有の関数、ストアドプロシージャ、またはLINQではうまく変換できないパフォーマンス重視のクエリなど、Raw SQLが必要になるシナリオもあります。
EF Coreには、セキュリティを損なわずにRaw SQLを安全に使用する方法が用意されています。
FromSqlRaw:安全なパラメーター化クエリ
FromSqlRawはRaw SQLを実行し、結果をエンティティにマッピングします。SQLインジェクションを防ぐため、必ず{0}プレースホルダーを使用し、文字列連結は決して行わないでください。
// 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 automaticallyFromSql後のLINQ合成
FromSqlの後にLINQ演算子を連結できます。EF CoreはRaw 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));DMLに対するExecuteSqlInterpolated
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のエンティティではない任意のスカラー型やDTO型をクエリできます。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()を使って、データベースビューやRaw SQLの結果をキーなしエンティティ型にマッピングできます。主キーがなくても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、またはEF CoreのLINQを使って、必ずパラメーター化クエリを使用してください。
// 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はストアドプロシージャの出力パラメーターをネイティブにはサポートしていません。その場合は、_db.Database.GetDbConnection()を使ってADO.NETに切り替えます。
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のほうが安全なのはなぜですか?
まとめ:EF CoreでのRaw SQL
要点:
FromSqlRaw:{0}プレースホルダーを使用し、ユーザー入力を決して連結しないFromSqlInterpolated:安全な補間文字列 → ADO.NETパラメーター- FromSqlの後にLINQ演算子を連結して、フィルタリング、並べ替え、ページングを追加する
ExecuteSqlInterpolatedAsync:安全なDML実行SqlQuery<T>:エンティティ以外のDTOをクエリする(.NET 7以降)- 出力パラメーターや複雑なストアドプロシージャには、ADO.NETを直接使用する
よくある質問
「Raw SQL、ストアドプロシージャと補間」レッスンは無料ですか?
はい。「Raw SQL、ストアドプロシージャと補間」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C# Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C# Academyコースには全4レッスンが含まれています。
「Raw SQL、ストアドプロシージャと補間」で何を学びますか?
FromSqlRaw、FromSqlInterpolated、ストアドプロシージャでRaw SQLを安全に実行し、インジェクションを防ぎます。 ブラウザで直接実行するハンズオンコードでC# Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
C# Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのC# Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。
「Raw SQL、ストアドプロシージャと補間」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このC# Academyレッスンでコードを書いて実行できますか?
はい。すべてのC# Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- Eager、Lazy、Explicit Loading
- Raw SQL、ストアドプロシージャと補間
- コンパイル済みクエリとパフォーマンス
- グローバルクエリフィルターと所有エンティティ