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

kai*_*lya 20 .net c# nhibernate

我使用一个简单的拦截器拦截nhibernate生成的sql字符串用于登录目的,它工作正常.

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

但是这会捕获没有参数值的sql语句.它们被'?'取代

例如:....在哪里USER0_.USERNAME =?

到目前为止,我发现的唯一替代方法是使用log4nets nhibernate.sql appender,它记录包含参数值的sql语句,但这并不能很好地为我服务.

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

我怎样才能获得完整的sql语句,包括nhibernate在运行时生成的参数值?

Chr*_*n.K 5

这是(大致草拟)我所做的:

  1. 创建一个IDbCommand接口的自定义实现,它在内部委托所有实际工作SqlCommand(假设它是LoggingDbCommand为了讨论的目的而调用).

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

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

请注意,我为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!
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,这些内容是在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.
}
Run Code Online (Sandbox Code Playgroud)

  • 值得注意的是,这种方法可能在NHibernate 3.2中无法实现.我尝试执行您描述的操作并获得以下异常:System.InvalidCastException:无法将类型为"MyApp.InterceptingCommand"的对象强制转换为"System.Data.SqlClient.SqlCommand".在NHibernate.AdoNet.SqlClientBatchingBatcher.AddToBatch(IExpectation期望)...我没有查看源来确定这个的确切原因,但看起来有一个期望从SqlClientDriver继承的驱动程序将创建SqlCommands. (3认同)

Fir*_*iro 0

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