目錄
- .NET Core實用技巧(一)如何將EF Core生成的SQL語句顯示在控制台中
前言
筆者最近在開發和維護一個.NET Core項目,其中使用幾個非常有意思的.NET Core相關的擴展,在此總結整理一下。
EF Core性能調優
如果你的項目中使用了EF Core, 且正在處於性能調優階段,那么了解EF Core生成的SQL語句是非常關鍵的。那么除了使用第三方工具,如何查看EF Core生成的SQL語句呢?這里筆者將給出一個基於.NET Core內置日志組件的實現方式。
創建一個實例項目
我們首先建一個控制台程序,在主程序中我們編寫了一個最簡單的EF查詢。
class Program {
static void Main (string[] args) {
var dbOptionBuilder = new DbContextOptionsBuilder<MyDbContext>();
dbOptionBuilder
.UseMySql("server=localhost;port=3306;database=EFCoreSampleDB;userid=root;pwd=a@12345");
using (var dbContext = new MyDbContext(dbOptionBuilder.Options)) {
var query = dbContext.Users.ToList();
}
}
}
這里為了演示,我們提前創建了一個MySql
數據庫,並在項目中創建了一個對應的EF Core上下文。當前上下文中只有一個User
實體,該實體只有2個屬性UserId
和UserName
。
public class MyDbContext : DbContext {
public MyDbContext (DbContextOptions<MyDbContext> options) : base (options) {
}
public DbSet<User> Users { get; set; }
}
public class User
{
[Key]
public Guid UserId { get; set;}
public string UserName { get; set;}
}
如何生成的SQL語句輸出到控制台?
.NET Core中提供了非常完善的日志接口。這里為了和.NET Core的日志接口集成,我們需要實現2個接口,一個是日志提供器接口ILoggerProvider
, 一個是日志接口ILogger
EFLoggerProvider.cs
public class EFLoggerProvider : ILoggerProvider {
public ILogger CreateLogger (string categoryName) => new EFLogger (categoryName);
public void Dispose () { }
}
EFLoggerProvider
的代碼非常的簡單,就是直接返回一個我們后續創建的EFLogger
對象。
EFLogger.cs
public class EFLogger : ILogger {
private readonly string categoryName;
public EFLogger (string categoryName) => this.categoryName = categoryName;
public bool IsEnabled (LogLevel logLevel) => true;
public void Log<TState> (LogLevel logLevel,
EventId eventId,
TState state,
Exception exception,
Func<TState, Exception, string> formatter) {
var logContent = formatter (state, exception);
Console.WriteLine ();
Console.WriteLine (logContent);
}
}
public IDisposable BeginScope<TState> (TState state) => null;
}
這里我們主要使用了內置的formatter
格式化了日志信息。
最后我們還需要將自定義的日志處理類和EF Core集成起來。這里我們需要復寫上下文類的OnConfiguring
方法。在其中通過UseLoggerFactory
方法,將我們自定義的日志處理類和EF Core的日志系統關聯起來。
public class MyDbContext : DbContext {
public MyDbContext (DbContextOptions<MyDbContext> options) : base (options) {
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
var loggerFactory = new LoggerFactory ();
loggerFactory.AddProvider(new EFLoggerProvider());
optionsBuilder.UseLoggerFactory(loggerFactory);
base.OnConfiguring(optionsBuilder);
}
public DbSet<User> Users { get; set; }
}
下面我們啟動項目,看一下效果。這里日志信息正確的顯示出來了。
PS: 如果項目中使用了通用主機或者ASP.NET Core, 你也可以在服務配置部分,通過
DbContextOptions
參數配置。services.AddDbContext<MyDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("MyDb")) .UseLoggerFactory(new LoggerFactory()));
如何去除無關日志?
在前面的步驟中,我們成功的輸出了查詢語句,但是有一個問題是我們只想查看輸出的SQL語句,其他的信息我們都不想要,那么能不能去除掉這些無關日志呢?答案是肯定的。
我們可以在Log
方法中,通過分類名稱,只輸出Microsoft.EntityFrameworkCore.Database.Command
分類下的日志,該日志即生成的SQL語句部分。
public void Log<TState> (LogLevel logLevel,
EventId eventId,
TState state,
Exception exception,
Func<TState, Exception, string> formatter)
{
if (categoryName == DbLoggerCategory.Database.Command.Name &&
logLevel == LogLevel.Information) {
var logContent = formatter (state, exception);
Console.WriteLine ();
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine (logContent);
Console.ResetColor ();
}
}
這里我們也做了一些其他的操作,通過修改控制台輸出文本的顏色,高亮了生成的SQL語句。重新啟動項目之后,效果如下。
如何顯示敏感數據?
這里看似我們已經完成了EF Core的語句輸出,但是在實際使用中,你還會遇到另外一個問題。
下面我們修改一下我們的主程序,我們嘗試插入一條User
信息。
class Program {
static void Main (string[] args) {
var dbOptionBuilder = new DbContextOptionsBuilder<MyDbContext> ();
dbOptionBuilder.UseMySql ("server=localhost;port=3306;database=EFCoreSampleDB;userid=root;pwd=a@12345");
using (var dbContext = new MyDbContext (dbOptionBuilder.Options)) {
dbContext.Users.Add(new User { UserId = Guid.NewGuid(), UserName = "Lamond Lu"});
dbContext.SaveChanges();
}
}
}
重新運行程序,你會得到一下結果。
這里你可能會問為什么不顯示@p0, @p1參數的值。這里是原因是為了保護敏感數據,EF Core默認關閉的敏感數據的顯示配置,如果你想要查看敏感數據,你需要通過DbContextOptionsBuilder
對象的EnableSensitiveDataLogging
方法修改敏感數據日志配置。
protected override void OnConfiguring (DbContextOptionsBuilder optionsBuilder) {
var loggerFactory = new LoggerFactory ();
loggerFactory.AddProvider (new EFLoggerProvider ());
optionsBuilder.EnableSensitiveDataLogging (true);
optionsBuilder.UseLoggerFactory (loggerFactory);
base.OnConfiguring (optionsBuilder);
}
重新啟動項目之后,你就能看到@p0, @p1參數的值了。