c# - usar - Interceptar sentencias SQL que contienen valores de parámetros generados por NHibernate
sql server sqlcommand parameters (4)
Aquí está (a grandes rasgos) lo que hice:
Cree una implementación personalizada de la interfaz
IDbCommand
, que delega internamente todo el trabajo real aSqlCommand
(suponga que se llamaLoggingDbCommand
para fines de discusión).Cree una clase derivada de la clase NHibernate
SqlClientDriver
. Debería verse algo como esto:public class LoggingSqlClientDriver : SqlClientDriver { public override IDbCommand CreateCommand() { return new LoggingDbCommand(base.CreateCommand()); } }
Registre su controlador de cliente en la configuración de NHibernate (consulte la documentación de NHibernate para obtener más información).
Eso sí, hice todo esto para NHibernate 1.1.2, por lo que es posible que se requieran algunos cambios para las versiones más nuevas. Pero supongo que la idea en sí seguirá funcionando.
OK, la verdadera carne estará en su implementación de LoggingDbCommand
. Solo le presentaré algunos ejemplos de implementaciones de métodos, pero creo que obtendrá la imagen y puede hacer lo mismo con los otros métodos de Ejecución * ():
public int ExecuteNonQuery()
{
try
{
// m_command holds the inner, true, SqlCommand object.
return m_command.ExecuteNonQuery();
}
catch
{
LogCommand();
throw; // pass exception on!
}
}
Las agallas están, por supuesto, en el método LogCommand (), en el que tiene "acceso completo" a todos los detalles del comando ejecutado:
- El texto del comando (con los marcadores de posición del parámetro como se especifica) a través de
m_command.CommandText
- Los parámetros y sus valores a través de la colección
m_command.Parameters
Lo que queda por hacer (lo he hecho pero no puedo publicar debido a los contratos - cojo pero cierto, lo siento) es reunir esa información en una cadena SQL adecuada (sugerencia: no se moleste en reemplazar los parámetros en el comando texto, simplemente enumérelos debajo como lo hace el propio registrador de NHibernate).
Barra lateral: es posible que desee abstenerse incluso de intentar iniciar sesión si la excepción es algo que se considera fatal (AccessViolationException, OOM, etc.) para asegurarse de que no empeore las cosas al intentar iniciar sesión en la cara de algo que ya es bastante catastrófico. .
Ejemplo:
try
{
// ... same as above ...
}
catch (Exception ex)
{
if (!(ex is OutOfMemoryException || ex is AccessViolationException || /* others */)
LogCommand();
throw; // rethrow! original exception.
}
Utilizo un interceptor simple para interceptar la cadena sql que nhibernate genera para fines de registro y funciona bien.
public class SessionManagerSQLInterceptor : EmptyInterceptor, IInterceptor
{
NHibernate.SqlCommand.SqlString IInterceptor.OnPrepareStatement(NHibernate.SqlCommand.SqlString sql)
{
NHSessionManager.Instance.NHibernateSQL = sql.ToString();
return sql;
}
}
Sin embargo, esto captura la instrucción sql sin los valores de los parámetros. Se reemplazan por ''?''
Por ejemplo: .... DONDE USUARIO0_.USERNAME =?
El único enfoque alternativo que encontré hasta ahora es el uso de log4nets nhibernate.sql appender que registra sentencias de SQL incluyendo valores de parámetros, pero eso no me está sirviendo bien.
Necesito usar un interceptor para que, por ejemplo, cuando detecto una excepción, deseo registrar la instrucción SQL específica que causó el problema de persistencia, incluidos los valores que contenía y el registro, enviarlo por correo, etc. Esto acelera mucho la depuración en comparación con entrar en los archivos de registro en busca de la consulta que causó el problema ..
¿Cómo puedo obtener sentencias SQL completas que incluyan valores de parámetros que nhibernate genere en tiempo de ejecución?
Es más simple (y funciona para todas las versiones de NH) hacer esto:
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;
}}
Implementar ILoggerFactory
personalizado y filtrar en LoggerFor
para que keyName
igual a NHibernate.SQL
y establecerlo a través de LoggerProvider.SetLoggersFactory
. Funciona para el controlador SQLite, debería funcionar para otro.
LoggerProvider
crea de forma predeterminada log4net
través de la reflexión si se presenta el ensamblado log4net
. Por lo tanto, la mejor implementación sería si ILoggerFactory
personalizado ILoggerFactory
inicio de sesión en el predeterminado, hasta que se solicite el registro NHibernate.SQL
.
Solo una idea: ¿puedes implementar un nuevo log4net-appender, que toma todas las sentencias (depuración con parámetro) y guarda la última? Cuando se produce un error, puede pedirle la última instrucción sql y enviarla por correo electrónico.