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

2023-09-02 11:56:22 作者:白首不分离是个笑话

我用一个简单的拦截器拦截的SQL字符串,NHibernate的生成登录电子的目的,它工作正常。

I use a simple interceptor to intercept the sql string that nhibernate generates for loging purposes and it works fine.

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

然而,这抓住了SQL语句,而不用参数值。他们被换成'?

This however captures the sql statement without the parameter values.. They are replaced by '?'

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

Eg: .... WHERE USER0_.USERNAME = ?

唯一的替代方法我发现迄今使用log4nets nhibernate.sql附加器它记录SQL语句,包括参数值,但是,这不符合我的好..

The only alternative approach i found so far is using log4nets nhibernate.sql appender which logs sql statement including parameter values but that is not serving me well..

我需要使用的拦截器,以便为如。当我抓到一个例外,我要记录导致持久性问题的具体的SQL语句,包括它包含的值并记录它邮寄等,这加速了debuging大量相比,进入日志文件中寻找查询造成的问题..

I need to use an interceptor so that for eg. when i catch an exception i want to log the specific sql statement that caused the persistence problem including the values it contained and log it mail it etc. This speeds up debuging great deal compared to going into log files looking for the query that caused the problem..

我怎样才能得到完整的SQL语句,包括参数值NHibernate的生成运行?

How can i get full sql statements including parameter values that nhibernate generate on runtime?

推荐答案

下面是(大致勾勒)我做了什么:

Here is (roughly sketched) what I did:

创建一个自定义实现的 IDbCommand的界面中,在内部代表一切以实际工作为的SqlCommand (假设它被称为 LoggingDbCommand 为讨论的​​目的)。

Create a custom implementation of the IDbCommand interface, which internally delegates all to the real work to SqlCommand (assume it is called LoggingDbCommand for the purpose of discussion).

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

Create a derived class of the NHibernate class SqlClientDriver. It should look something like this:

public class LoggingSqlClientDriver : SqlClientDriver
{
    public override IDbCommand CreateCommand()
    {
        return new LoggingDbCommand(base.CreateCommand());
    }
}

注册在NHibernate的配置你的客户端驱动程序(见NHibernate的文档获取详细信息)。

Register your Client Driver in the NHibernate Configuration (see NHibernate docs for details).

你的,我做这一切都为NHibernate的1.1.2心灵所以有可能是需要的较新版本的一些变化。但我想这个想法本身仍然是工作。

Mind you, I did all this for NHibernate 1.1.2 so there might be some changes required for newer versions. But I guess the idea itself will still be working.

OK,真正的肉会在你执行 LoggingDbCommand 的。我只会起草你一些例子方法实现,但我猜你会得到的图片,并能为其他执行*也这样做()方法​​:

OK, the real meat will be in your implementation of LoggingDbCommand. I will only draft you some example method implementations, but I guess you'll get the picture and can do likewise for the other Execute*() methods.:

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

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

The guts are, of course, in the LogCommand() method, in which you have "full access" to all the details of the executed command:

m_command.CommandText 命令文本(带有参数占位符像指定) 通过对 m_command.Parameters 集合中的参数和值 The command text (with the parameter placeholders in it like specified) through m_command.CommandText The parameters and their values through to the m_command.Parameters collection

什么剩下要做的(我已经做到了,但不能因合同后 - 蹩脚,但却是事实,抱歉)是组装的信息转换为适当的SQL字符串(提示:不要打扰更换参数在命令文本,只列出它们下面像NHibernate的自己的记录器一样)。

What is left to do (I've done it but can't post due to contracts - lame but true, sorry) is to assemble that information into a proper SQL-string (hint: don't bother replacing the parameters in the command text, just list them underneath like NHibernate's own logger does).

补充工具栏::您可能需要从甚至试图登录时,如果该异常是认为是致命的(AccessViolationException,OOM等),以确保你不要让事情变得更糟,试图避免登录某事的脸已经pretty的灾难性的。

Sidebar: You might want to refrain from even attempting to log if the the exception is something considered fatal (AccessViolationException, OOM, etc.) to make sure you don't make things worse by trying to log in the face of something already pretty catastrophic.

例如:

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

   throw;  // rethrow! original exception.
}