在C#项目中使用EF框架进行数据库操作时,很多时候我们需要查看EF自动生成的SQL语句,用来排查查询性能问题或者验证数据操作逻辑是否正确。EF框架本身内置了日志输出能力,也支持自定义拦截器来捕获执行的SQL语句,下面分别介绍两种常用的实现方式。

方法一:通过DbContext配置日志输出
EF Core的DbContext类提供了LogTo方法,可以直接将框架生成的日志输出到指定的目标,比如控制台、文件或者自定义的委托中。这种方式配置简单,适合快速查看SQL语句的场景。
实现步骤
首先需要在自定义的DbContext类中重写OnConfiguring方法,调用LogTo方法指定日志输出的逻辑。如果需要过滤只输出SQL相关的日志,可以指定日志类别为DbLoggerCategory.Database.Command.Name。
以下是完整的代码示例:
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
// 自定义DbContext类
public class AppDbContext : DbContext
{
// 数据库集合示例
public DbSet<User> Users { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// 配置数据库连接字符串
optionsBuilder.UseSqlServer("Server=.;Database=TestDB;Trusted_Connection=True;");
// 配置日志输出,只捕获数据库命令相关的日志(即SQL语句)
optionsBuilder.LogTo(
// 自定义日志处理逻辑,这里将SQL输出到控制台
log => Console.WriteLine($"执行SQL: {log}"),
// 指定日志过滤条件,只输出数据库命令类别的日志
new[] { DbLoggerCategory.Database.Command.Name },
// 日志级别设置为信息级别,SQL语句属于信息级别日志
LogLevel.Information
);
}
}
// 用户实体类示例
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
// 使用示例
class Program
{
static void Main(string[] args)
{
using (var db = new AppDbContext())
{
// 执行查询操作,此时会自动输出对应的SQL语句
var userList = db.Users.Where(u => u.Age > 18).ToList();
}
}
}
注意事项
LogTo方法会在每次执行数据库命令时触发,日志内容不仅包含SQL语句,还会包含参数信息,方便查看完整的执行逻辑。- 如果不需要输出所有SQL语句,可以在委托中添加过滤逻辑,比如只输出执行时间超过指定阈值的SQL。
方法二:自定义IDbCommandInterceptor拦截器
如果需要更灵活的SQL拦截能力,比如将SQL语句存储到数据库、发送到远程服务,或者修改SQL执行逻辑,可以使用EF Core提供的IDbCommandInterceptor接口,实现自定义的命令拦截器。
实现步骤
首先创建一个实现IDbCommandInterceptor接口的类,重写接口中对应的方法,比如ReaderExecuting方法会在查询命令执行前触发,ReaderExecuted方法会在查询命令执行后触发。然后将自定义的拦截器注册到DbContext中即可。
以下是完整的代码示例:
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using System;
using System.Data.Common;
// 自定义SQL命令拦截器
public class CustomSqlInterceptor : IDbCommandInterceptor
{
// 查询命令执行前触发
public void ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
{
// 获取即将执行的SQL语句和参数
string sql = command.CommandText;
Console.WriteLine($"即将执行SQL: {sql}");
// 可以遍历参数输出参数信息
foreach (DbParameter param in command.Parameters)
{
Console.WriteLine($"参数名: {param.ParameterName}, 参数值: {param.Value}");
}
}
// 查询命令执行后触发
public void ReaderExecuted(DbCommand command, CommandExecutedEventData eventData, DbDataReader result)
{
// 可以获取执行耗时等信息
if (eventData.Duration != null)
{
Console.WriteLine($"SQL执行耗时: {eventData.Duration.Value.TotalMilliseconds} 毫秒");
}
}
// 非查询命令(增删改)执行前触发
public void NonQueryExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<int> result)
{
Console.WriteLine($"即将执行非查询SQL: {command.CommandText}");
}
// 非查询命令执行后触发
public void NonQueryExecuted(DbCommand command, CommandExecutedEventData eventData, int result)
{
Console.WriteLine($"非查询SQL执行完成,影响行数: {result}");
}
}
// 自定义DbContext类,注册拦截器
public class AppDbContextWithInterceptor : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// 配置数据库连接字符串
optionsBuilder.UseSqlServer("Server=.;Database=TestDB;Trusted_Connection=True;");
// 注册自定义拦截器
optionsBuilder.AddInterceptors(new CustomSqlInterceptor());
}
}
// 使用示例
class Program2
{
static void Main(string[] args)
{
using (var db = new AppDbContextWithInterceptor())
{
// 执行查询操作,会触发拦截器的ReaderExecuting和ReaderExecuted方法
var userList = db.Users.Where(u => u.Age > 18).ToList();
// 执行新增操作,会触发拦截器的NonQueryExecuting和NonQueryExecuted方法
db.Users.Add(new User { Name = "张三", Age = 20 });
db.SaveChanges();
}
}
}
拦截器方法说明
| 方法名 | 触发时机 | 适用场景 |
|---|---|---|
| ReaderExecuting / ReaderExecuted | 查询命令执行前/后 | 拦截SELECT语句,获取查询SQL和执行耗时 |
| NonQueryExecuting / NonQueryExecuted | 非查询命令执行前/后 | 拦截INSERT、UPDATE、DELETE语句,获取操作SQL和影响行数 |
| ScalarExecuting / ScalarExecuted | 返回单个值的命令执行前/后 | 拦截返回单个值的SQL语句,比如COUNT查询 |
两种方法的适用场景对比
如果只是临时需要查看SQL语句,或者只需要简单的日志输出,优先选择第一种LogTo配置的方式,实现成本低,代码改动小。如果需要在SQL执行前后做更多自定义处理,比如持久化SQL日志、统计SQL执行频率、监控慢查询等,第二种自定义拦截器的方式更合适,扩展性更强。
在实际开发中,可以根据项目的具体需求选择合适的实现方式,两种方式也可以同时使用,互不冲突。
C#_EFSQL_statement_interceptionDatabase_LogEF_CoreSQL_monitoring修改时间:2026-06-10 10:09:41