导读:本期聚焦于小伙伴创作的《C#怎么获取当前执行的SQL语句?C#如何拦截EF框架生成的日志》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《C#怎么获取当前执行的SQL语句?C#如何拦截EF框架生成的日志》有用,将其分享出去将是对创作者最好的鼓励。

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

C#怎么获取当前执行的SQL语句?C#如何拦截EF框架生成的日志

方法一:通过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

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。