Подтвердить что ты не робот

Перехватывать операторы SQL, содержащие значения параметров, генерируемые NHibernate

Я использую простой перехватчик для перехвата строки sql, которую nhibernate генерирует для целей ведения журнала, и работает нормально.

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 генерирует во время выполнения?

4b9b3361

Ответ 1

Вот (грубо набросок), что я сделал:

  • Создайте пользовательскую реализацию интерфейса 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).

Боковая панель:. Возможно, вы захотите воздержаться от попыток записи, если исключение является чем-то признанным фатальным (AccessViolationException, OOM и т.д.), чтобы убедиться, что вы не делаете хуже, пытаясь чтобы войти в лицо чего-то уже довольно катастрофического.

Пример:

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

   throw;  // rethrow! original exception.
}

Ответ 2

Это проще (и работает для всех версий 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

Просто идея: вы можете реализовать новый log4net-appender, который принимает все sqlstatements (debug with parameter) и содержит последний. Когда произошла ошибка, вы можете попросить его о последнем sqlstatement и отправить его по электронной почте.

Ответ 4

Внесите пользовательские ILoggerFactory и фильтруйте в LoggerFor для keyName равно NHibernate.SQL и установите через LoggerProvider.SetLoggersFactory. Работает для драйвера SQLite, должен работать и для других.

LoggerProvider по умолчанию создает log4net через отражение, если представлен сборник log4net. Лучше всего реализовать, если пользовательский ILoggerFactory будет делегировать журнал по умолчанию, пока не будет запрошен запрос NHibernate.SQL.