觀察 EF Core 產生的 SQL 指令
5 |
如果你跟我一樣,過去在專案裡都是自己連資料庫寫 SQL 指令,轉用 EF Core 之後,免不了會想確認 Where().Select() 產生的 SQL 查詢是否有效率,好奇 SaveChanges() 背後 EF Core 是怎麼更新資料庫?
要解答上述疑惑,最好的方法莫過於啟用 Logging 功能,直接觀察 EF Core 產生的每一條 SQL 指令。做法很簡單,Startup.ConfigureServices() AddDbContextPool() 時呼叫 .UseLoggerFactory(),傳入 ILoggerFactory,若要將結果輸出到 Console,可使用 LoggerFactory.Create(builder => builder.AddConsole()) 建立將結果顯示在 Console 的 ILoggerFactory 物件:
public void ConfigureServices(IServiceCollection services)
{
//註冊 DB Context,指定使用 SQL 資料庫
services.AddDbContextPool<JournalDbContext>(options =>
{
//TODO: 實際應用時,連線字串不該寫死在程式碼裡,應應移入設定檔並加密儲存
options.UseSqlServer(Configuration.GetConnectionString("LocalDB"))
//啟用 Logging 觀察 SQL 指令
.UseLoggerFactory(LoggerFactory.Create(builder => builder.AddConsole()));
});
services.AddRazorPages();
}
也可以指定同時輸出到多個管道,例如 Console 跟 Debug.WriteLine():
options.UseSqlServer(Configuration.GetConnectionString("LocalDB"))
//啟用 Logging 觀察 SQL 指令
.UseLoggerFactory(LoggerFactory.Create(builder =>
{
builder.AddConsole().AddDebug();
}));
這樣,我們就能觀察到 LINQ 查詢跟 SaveChanges() 所對應的 SQL 指令,印證 EF Core 更新衝突處理策略 文章裡提到的: 1) EF Core 會追蹤屬性狀態,只更新有異動的欄位 2) RowVersion 欄位會被當成 WHERE 條件,用以偵測資料是否被他人異動。
不過預設 Log 只會看到 SQL 指令,不包含參數內容,如果要連參數一起顯示,在 UseLoggerFactory() 後方加上 EnableSensitiveDataLogging():
options.UseSqlServer(Configuration.GetConnectionString("LocalDB"))
//啟用 Logging 觀察 SQL 指令
.UseLoggerFactory(LoggerFactory.Create(builder =>
{
builder.AddConsole().AddDebug();
}))
.EnableSensitiveDataLogging();
啟用後,輸出結果將包含參數內容:
但請務必注意,顯示參數功能偏向偵錯抓蟲的非常手段(所以預設是關閉的),在線上環境不應常態啟用。參數內容可能包含客戶個資、信用卡號等機敏資料,當心別讓 Log 變成資料外洩的來源,
Tips of how to enable EF Core logging to monitor SQL statement and parameters.
Comments
# by Chris Torng
感覺很麻煩,我是在 appsettings.Development.json 中加入設定即可在 log 中看到 (開發期才需要確認 SQL 指令是否無異常,部署後即不需確認): "Logging": { "LogLevel": { "Microsoft.EntityFrameworkCore": "Information" } }
# by Jeffrey
to Chris Torng,這招好,感謝分享。(但沒能查到這個設定細節的文件說明,找時間來調查一下)
# by Ho.Chun
在 console 類型的專案,這些設定很好用
# by Ho.Chun
請問 builder.AddDebug() 的輸出會在哪 ?
# by Jeffrey
to Ho.Chun, 會跟 Debug.WriteLine() 一樣輸出到 Output 偵錯視窗。