拦截包含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语句。它们被’?’取代
例如:….在哪里USER0_.USERNAME =?
到目前为止,我发现的唯一替代方法是使用log4nets nhibernate.sql appender,它记录包含参数值的sql语句,但这并不能很好地为我服务。
我需要使用一个拦截器,以便例如。 当我捕获exception时,我想记录导致持久性问题的特定sql语句,包括它包含的值,并将其记录到邮件等等。与进入日志文件寻找导致问题的查询相比,这加速了极大的优势..
我怎样才能获得完整的sql语句,包括nhibernate在运行时生成的参数值?
这是(大致草拟)我所做的:
-
创建
IDbCommand
接口的自定义实现,该接口在内部将所有实际工作委托给SqlCommand
(假设它被称为LoggingDbCommand
用于讨论)。 -
创建NHibernate类
SqlClientDriver
的派生类。 它应该看起来像这样:public class LoggingSqlClientDriver : SqlClientDriver { public override IDbCommand CreateCommand() { return new LoggingDbCommand(base.CreateCommand()); } }
-
在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.CommandText
- 参数及其值到
m_command.Parameters
集合
还剩下什么(我已经完成但由于合同无法发布 – 跛脚但是真实,抱歉)是将该信息组合成一个正确的SQL字符串(提示:不要费心替换命令中的参数)文本,只需将它们列在NHibernate自己的记录器下面就可以了。
补充工具栏:您可能希望避免甚至尝试记录,如果exception被认为是致命的(AccessViolationException,OOM等),以确保您不会因为尝试登录已经非常灾难性的事情而使事情变得更糟。
例:
try { // ... same as above ... } catch (Exception ex) { if (!(ex is OutOfMemoryException || ex is AccessViolationException || /* others */) LogCommand(); throw; // rethrow! original exception. }
这样做更简单(适用于所有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; }}
只是一个想法:你能实现一个新的log4net-appender,它接受所有sqlstatements(带参数调试)并保存最后一个。 当发生错误时,您可以向他询问最后一个sqlstatement并通过电子邮件发送。
在LoggerFor
实现自定义ILoggerFactory
和filter,以便keyName
等于NHibernate.SQL
并通过LoggerProvider.SetLoggersFactory
设置。 适用于SQLite驱动程序,应该适用于其他。
如果显示log4net
程序集, LoggerProvider
默认通过reflection创建log4net
。 因此,最好的实现是自定义ILoggerFactory
将日志委托为默认值,直到请求NHibernate.SQL
日志。