拦截包含NHibernate生成的参数值的SQL语句

时间:2011-05-18 17:44:11

标签: c# .net nhibernate

我使用一个简单的拦截器来拦截nhibernate为了记录而生成的sql字符串,它工作正常。

public class SessionManagerSQLInterceptor : EmptyInterceptor, IInterceptor
    {
        NHibernate.SqlCommand.SqlString IInterceptor.OnPrepareStatement(NHibernate.SqlCommand.SqlString sql)
        {
            NHSessionManager.Instance.NHibernateSQL = sql.ToString();
            return sql;
        }
    }

然而,它捕获没有参数值的sql语句。它们被'?'

替换

例如:.... WHERE USER0_.USERNAME =?

到目前为止,我发现的唯一替代方法是使用log4nets nhibernate.sql appender,它记录包含参数值的sql语句,但这对我不起作用..

我需要使用一个拦截器,以便例如。当我捕获异常时,我想记录导致持久性问题的特定sql语句,包括它包含的值并将其记录到邮件等等。与进入日志文件寻找导致问题的查询相比,这加速了极大的优势..

如何获得完整的sql语句,包括nhibernate在运行时生成的参数值?

4 个答案:

答案 0 :(得分:5)

这是(粗略勾勒出)我的所作所为:

  1. 创建IDbCommand接口的自定义实现,该实现在内部将所有实际工作委托给SqlCommand(假设它被称为LoggingDbCommand以供讨论)。

  2. 创建NHibernate类SqlClientDriver的派生类。看起来应该是这样的:

    public class LoggingSqlClientDriver : SqlClientDriver
    {
        public override IDbCommand CreateCommand()
        {
            return new LoggingDbCommand(base.CreateCommand());
        }
    }
    
  3. 在NHibernate配置中注册您的客户端驱动程序(有关详细信息,请参阅NHibernate文档)。

  4. 请注意,我为NHibernate 1.1.2做了所有这些,因此新版本可能需要进行一些更改。但我想这个想法本身仍然有用。

    好的,真正的肉将在你LoggingDbCommand的实施中。我只会草拟一些示例方法实现,但我想你会得到图片,并且可以为其他Execute *()方法做同样的事情。:

    public int ExecuteNonQuery()
    {
        try
        {
            // m_command holds the inner, true, SqlCommand object.
            return m_command.ExecuteNonQuery();
        }
        catch
        {
            LogCommand();
            throw; // pass exception on!
        }
    }
    

    当然,这些内容在LogCommand()方法中,您可以“完全访问”所执行命令的所有细节:

    • 命令文本(参数中的参数占位符,如指定)通过m_command.CommandText
    • 参数及其值到m_command.Parameters集合

    剩下要做的事情(我已经完成但由于合同而无法发布 - 跛脚但是真实,对不起)是将该信息组合成一个正确的SQL字符串(提示:不要费心更换参数在命令文本中,只需在NHibernate自己的记录器下面列出它们。

    补充工具栏:如果异常被认为是致命的(AccessViolationException,OOM等),您可能希望避免尝试记录,以确保您不会因为尝试而使事情变得更糟登录已经非常灾难性的事情。

    示例:

    try
    {
       // ... same as above ...
    }
    catch (Exception ex)
    {
       if (!(ex is OutOfMemoryException || ex is AccessViolationException || /* others */)
         LogCommand();
    
       throw;  // rethrow! original exception.
    }
    

答案 1 :(得分:3)

这样做更简单(适用于所有NH版本):

public class LoggingSqlClientDriver : SqlClientDriver
{
    public override IDbCommand GenerateCommand(CommandType type, NHibernate.SqlCommand.SqlString sqlString, NHibernate.SqlTypes.SqlType[] parameterTypes)
    {
        SqlCommand command = (SqlCommand)base.GenerateCommand(type, sqlString, parameterTypes);

        LogCommand(command);

        return command;
    }}

答案 2 :(得分:0)

只是一个想法:你能实现一个新的log4net-appender,它接受所有的sqlstatements(带参数调试)并保存最后一个。当发生错误时,您可以向他询问最后的sql语句并通过电子邮件发送。

答案 3 :(得分:0)

实施自定义ILoggerFactory并在LoggerFor中过滤keyName等于NHibernate.SQL并通过LoggerProvider.SetLoggersFactory进行设置。适用于SQLite驱动程序,适用于其他。

默认情况下,

LoggerProvider会在log4net程序集显示时通过反射创建log4net。因此,如果自定义ILoggerFactory将日志委托为默认值,那么最好的实现方式是,直到请求NHibernate.SQL日志。