如果你跟我一樣,過去在專案裡都是自己連資料庫寫 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 偵錯視窗。

Post a comment