在最近的幾個 Entity Framework Core 版本,對於 Logging (紀錄) 的撰寫方式一直在改變,大致上可區分成 EF Core 2.1 , EF Core 3.0+ 與 EF Core 5.0 三種版本,這意味著你從網路上找到的資訊,很有可能是舊的,但很多人無法區別其差異,因為舊的寫法到了 EF Core 5.0 依然可以用,只是會被標示棄用而已。你應該也可以想像得到,越新的 EF Core 版本,寫法就會更簡單的,這篇文章我就來說說幾種不同的紀錄方法。
為什麼要紀錄 EF Core 自動產生的 SQL 命令
由於 Entity Framework Core 是一套相當完整的 ORM 框架,我們可以利用 ORM 架構幫我們對資料進行建模(Modeling),用「物件導向」的方式描述一份資料,方便我們可以用相對「抽象」的方式來存取資料。不過,有些時候透過 ORM 框架自動產生的 SQL 語法,可能會導致執行效能低落,這時我們就可以透過 Logging 的方式,將 EF Core 自動產生的 SQL 命令印出來(或寫到檔案中),藉此進一步分析如何優化這些 SQL 語法的執行效率。
我們在做 Entity Framework 效能調教的時候,比較常見的方法是將複雜的查詢語法,改用預存程序(Stored Procedure)或檢視表(Views)來改善執行效率,但更多時候可能只是加入「索引」就可以大幅改善查詢效能。如果遇到需要批次新增、更新或刪除資料的情況,也會透過直接執行 SQL 的方式來執行,封裝成預存程序也是另一種可行的方案。
最簡單的紀錄方法
其實要紀錄 EF Core 自動產生的 SQL 命令,最簡單的方式,就是直接調整 Logging 的組態設定,而且從舊版 EF Core 到最新的 EF Core 5.0 都可以用。基本上你只需要設定兩個步驟:
- 編輯 appsettings.Development.json 組態設定
你只要在 Logging:LogLevel 加入 Microsoft.EntityFrameworkCore.Database.Command 並將其設定為 Information 即可:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.EntityFrameworkCore.Database.Command": "Information"
}
}
}
假設我們對 Course 實體查詢的程式碼如下:
var course = await _context.Courses.FindAsync(1));
預設輸出的 Log 內容,並不會包含實際傳到資料庫的查詢參數值( 1 ),只有 SQL 語法而已,這是因為 個資保* 造成的變化:
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (35ms) [Parameters=[@__p_0='?'], CommandType='Text', CommandTimeout='30']
SELECT TOP(1) [c].[CourseID], [c].[Credits], [c].[DepartmentID], [c].[Title]
FROM [Course] AS [c]
WHERE [c].[CourseID] = @__p_0
要顯示上述 @__p_0 的參數值,就要進一步調整程式碼。
- 編輯 Startup 類別的 ConfigureServices 方法
請將以下這段程式:
services.AddDbContext<ContosoUniversityContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
});
修改為以下,也就是加上 options.EnableSensitiveDataLogging(); 設定:
services.AddDbContext<ContosoUniversityContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
options.EnableSensitiveDataLogging();
});
啟用「敏感資料」紀錄的結果,你的紀錄內容就會變成這樣:
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (35ms) [Parameters=[@__p_0='1'], CommandType='Text', CommandTimeout='30']
SELECT TOP(1) [c].[CourseID], [c].[Credits], [c].[DepartmentID], [c].[Title]
FROM [Course] AS [c]
WHERE [c].[CourseID] = @__p_0
簡單兩個步驟,真的蠻實用的,各位可以參考看看!