2011-05-18 44 views
20

我使用一个简单的拦截器来拦截nhibernate为记录目的而生成的sql字符串,它工作正常。拦截包含由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在运行时生成的参数值?

+0

您如何知道生成的异常总是与正在运行的sql语句相关?另外,您似乎可以将您的sql语句日志记录集成到常规的nhibernate日志文件中,并且如果您已经生成了与datestamp/timestamp一起生成的异常,则查找sql​​语句不会很难。 – 2011-05-18 21:04:17

+0

我不知道,这是没有必要的,因为异常是默认情况下记录和SQL语句是另外,以防万一..查找日志和查找从时间戳查询正是我想要避免.. – kaivalya 2011-05-18 23:37:46

回答

0

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

5

这里是(大致勾勒)我做了什么:

  1. 创建IDbCommand接口,在内部代表一切以实际工作来SqlCommand(假设它被称为LoggingDbCommand为目的的自定义实现讨论)。

  2. 创建一个NHibernate类的派生类SqlClientDriver。它应该是这个样子:

    public class LoggingSqlClientDriver : SqlClientDriver 
    { 
        public override IDbCommand CreateCommand() 
        { 
         return new LoggingDbCommand(base.CreateCommand()); 
        } 
    } 
    
  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! 
    } 
} 

的胆量,当然,在LogCommand()方法中,你必须“完全访问权限”来执行命令的所有细节:

  • 命令文本(带通过对收集m_command.Parameters

参数占位符,就象一个指定)通过m_command.CommandText

  • 的参数和它们的值是什么是做的(我已经做了,但不能发布由于合同 - 跛脚但真实,对不起)是将这些信息组装成一个适当的SQL字符串(提示:不要更换命令文本中的参数,就像NHibernate自己的记录器一样在下面列出它们)。

    边栏:如果异常被认为是致命的(AccessViolationException,OOM等),您可能想要避免尝试登录,以确保您不会因尝试登录面对已经非常灾难性的事情。

    例子:

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

    值得注意的是,这种方法可能不会在NHibernate 3.2中实现。我试图做你所描述的并得到以下异常:System.InvalidCastException:无法强制类型'MyApp.InterceptingCommand'的类型的对象键入'System.Data.SqlClient.SqlCommand'。在NHibernate.AdoNet.SqlClientBatchingBatcher.AddToBatch(IExpectation期望)... 我还没有看过源确定这个确切的原因,但它看起来像有一个期望,从SqlClientDriver继承的驱动程序将创建SqlCommands 。 – 2011-10-17 15:01:22

    +1

    @DanMalcom:非常感谢您的支持。我们很快就会移植到3.2,并且可能会绊倒它;-) – 2011-10-18 04:27:50

    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; 
        }} 
    
    +3

    此时参数值不可用,例如,在原始示例中为USERNAME插入的值。 – 2011-10-14 16:05:41

    0

    LoggerForkeyName定制实现ILoggerFactory和过滤等于NHibernate.SQL,并通过LoggerProvider.SetLoggersFactory设置。适用于SQLite驱动程序,应该适用于其他。

    LoggerProvider默认情况下通过反射创建log4net如果log4net汇编呈现。因此,如果自定义ILoggerFactory将委托登录到默认设置,那么最好的实现方式是,直到NHibernate.SQL记录请求。