.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();
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 options) : base (options) {
}
public DbSet 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 (LogLevel logLevel,
EventId eventId,
TState state,
Exception exception,
Func formatter) {
var logContent = formatter (state, exception);
Console.WriteLine ();
Console.WriteLine (logContent);
}
}
public IDisposable BeginScope (TState state) => null;
}
这里我们主要使用了内置的formatter
格式化了日志信息。
最后我们还需要将自定义的日志处理类和 EF Core 集成起来。这里我们需要复写上下文类的OnConfiguring
方法。在其中通过UseLoggerFactory
方法,将我们自定义的日志处理类和 EF Core 的日志系统关联起来。
public class MyDbContext : DbContext {
public MyDbContext (DbContextOptions 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 Users { get; set; }
}
下面我们启动项目,看一下效果。这里日志信息正确的显示出来了。
PS: 如果项目中使用了通用主机或者 ASP.NET Core, 你也可以在服务配置部分,通过
DbContextOptions
参数配置。
services.AddDbContext
(options => options.UseSqlServer(Configuration.GetConnectionString("MyDb"))
.UseLoggerFactory(new LoggerFactory()));
如何去除无关日志?
在前面的步骤中,我们成功的输出了查询语句,但是有一个问题是我们只想查看输出的 SQL 语句,其他的信息我们都不想要,那么能不能去除掉这些无关日志呢?答案是肯定的。
我们可以在Log
方法中,通过分类名称,只输出Microsoft.EntityFrameworkCore.Database.Command
分类下的日志,该日志即生成的 SQL 语句部分。
public void Log (LogLevel logLevel,
EventId eventId,
TState state,
Exception exception,
Func formatter)
{
if (categoryName == "Microsoft.EntityFrameworkCore.Database.Command" &&
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 ();
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 参数的值了。