EF Core拦截器获取或修改SQL语句

618 阅读2分钟

最近在看EF Core官方文档时,发现有个叫拦截器的东西,那么这个拦截器有啥用呢?其实,一般情况下,我们可能也有用不到它。当我们需要操作EF Core执行的SQL语句或者操作数据库时,那么,拦截器就派上用场了。

在开始之前,我们先来认识三个拦截器接口

接口名用途基类
IDbCommandInterceptor创建命令、执行命令、命令失败、释放命令的 DbDataReaderDbCommandInterceptor
IDbConnectionInterceptor打开、关闭连接、连接失败DbConnectionInterceptor
IDbTransactionInterceptor创建事务、使用现有事务、提交事务、回滚事务、创建和使用保存点、事务失败DbTransactionInterceptor

基类包含对应接口中每个方法的实现。使用基类,则可以不需要实现每个接口方法。如果,我们想要获取执行的SQL语句,那么继承DbCommandInterceptor这个基类即可。这里,我们先写个简单的取数接口,来看看EF Core生成的默认SQL语句是什么样的

 public IEnumerable<User> Get()
        {
            return _demoContext.Users.ToList();
        }

我们知道FirstFirstOrDefault是取一条数据,但是现在偏要让前面多条查询只返回一条数据呢。这里,我们就新建一个拦截器继承DbCommandInterceptor这个基类,并重写ReaderExecuting方法

    public class ModifyDbCommandInterceptor: DbCommandInterceptor
    {
        public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
        {
            command.CommandText= command.CommandText.Trim()+" limit 1";
            return base.ReaderExecuting(command, eventData, result);
        }
    }

然后用AddInterceptors来注册我新加的拦截器

var serverVersion = new MySqlServerVersion(new Version(8, 0, 24));
string connectionString = builder.Configuration.GetConnectionString("MysqlConnectionString");
builder.Services.AddDbContext<DemoContext>(opt =>
{
    opt.UseMySql(connectionString, serverVersion);
    opt.AddInterceptors(new ModifyDbCommandInterceptor());
});

上图可以看到,我们修改的SQL产生了效果,当然,正常情况下肯定没人会这般无聊。那么什么时候,我们可能会用到拦截器呢?我大致想到这几种场景,如:改写SQL,有时候,EF Core生成的SQL可能不是我们想要的;缓存结果数据,有时候,我们可能想将查询的结果数据缓存起来。当然,通过其他方式来缓存查询结果也可以;审计操作日志,有时候,我们想要记录增删改的日志,等等。