49 votes

À l'aide de MiniProfiler la base de données de profilage avec NHibernate

Quelle est la façon la plus simple d'utiliser MiniProfilerde base de données de profilage avec NHibernate? Pour que le profiler pour le travail, j'ai besoin de mettre la DbConnection que NHibernate utilise en ProfiledDbConnection.

Je ne suis pas trop familier avec le fonctionnement interne de NHibernate, donc je ne sais pas où tous les points d'extensibilité. (J'ai remarqué qu'un NHibernate ISession a Connection de la propriété, mais il est en lecture seule.)

30voto

Robert Milne Points 306

[Mise à JOUR] Veuillez consulter les liens suivants pour une version qui utilise RealProxy de proxy SqlCommand - traitement par lots est maintenant pris en charge

J'ai laissé l'original de la réplique inchangée il a été accepté. [/Mise à JOUR]

J'ai réussi à partiellement obtenir que cela fonctionne par la mise en œuvre d'un Profilé Pilote Client (exemple pour Sql Server 2008 ci-dessous) - cela fonctionne pour de simples exemples, cependant, je n'ai pas encore trouvé une solution pour NH dosage (qui tente de lancer la commande de retour à SqlCommand)

public class ProfiledSql2008ClientDriver : Sql2008ClientDriver
{
    public override IDbCommand CreateCommand()
    {
        return new ProfiledDbCommand(
            base.CreateCommand() as DbCommand, 
            null,
            MiniProfiler.Current);
    }

    public override IDbConnection CreateConnection()
    {
        return ProfiledDbConnection.Get(
            base.CreateConnection() as DbConnection, 
            MiniProfiler.Current);
    }
}

10voto

Craig Points 15049

J'ai étendu Roberts réponse ci-dessus à travailler avec NHibernate de traitement par lots. Il y a beaucoup de code ici, donc il peut éventuellement être raccourcie, dont certaines sur la base de la nHibernate source pour le pilote client.

<property name="connection.driver_class">YoureOnTime.Data.ProfiledSqlClientDriver, YoureOnTime.Common</property>


public class ProfiledSqlClientDriver : DriverBase, IEmbeddedBatcherFactoryProvider
{
    public override IDbConnection CreateConnection()
    {
        return new ProfiledSqlDbConnection(
            new SqlConnection(), 
            MiniProfiler.Current);
    }

    public override IDbCommand CreateCommand()
    {
        return new ProfiledSqlDbCommand(
            new SqlCommand(),
            null,
            MiniProfiler.Current);
    }

    public override bool UseNamedPrefixInSql
    {
        get { return true; }
    }

    public override bool UseNamedPrefixInParameter
    {
        get { return true; }
    }

    public override string NamedPrefix
    {
        get { return "@"; }
    }

    public override bool SupportsMultipleOpenReaders
    {
        get { return false; }
    }

    public static void SetParameterSizes(IDataParameterCollection parameters, SqlType[] parameterTypes)
    {
        for (int i = 0; i < parameters.Count; i++)
        {
            SetVariableLengthParameterSize((IDbDataParameter)parameters[i], parameterTypes[i]);
        }
    }

    private const int MaxAnsiStringSize = 8000;
    private const int MaxBinarySize = MaxAnsiStringSize;
    private const int MaxStringSize = MaxAnsiStringSize / 2;
    private const int MaxBinaryBlobSize = int.MaxValue;
    private const int MaxStringClobSize = MaxBinaryBlobSize / 2;
    private const byte MaxPrecision = 28;
    private const byte MaxScale = 5;
    private const byte MaxDateTime2 = 8;
    private const byte MaxDateTimeOffset = 10;

    private static void SetDefaultParameterSize(IDbDataParameter dbParam, SqlType sqlType)
    {
        switch (dbParam.DbType)
        {
            case DbType.AnsiString:
            case DbType.AnsiStringFixedLength:
                dbParam.Size = MaxAnsiStringSize;
                break;

            case DbType.Binary:
                if (sqlType is BinaryBlobSqlType)
                {
                    dbParam.Size = MaxBinaryBlobSize;
                }
                else
                {
                    dbParam.Size = MaxBinarySize;
                }
                break;
            case DbType.Decimal:
                dbParam.Precision = MaxPrecision;
                dbParam.Scale = MaxScale;
                break;
            case DbType.String:
            case DbType.StringFixedLength:
                dbParam.Size = IsText(dbParam, sqlType) ? MaxStringClobSize : MaxStringSize;
                break;
            case DbType.DateTime2:
                dbParam.Size = MaxDateTime2;
                break;
            case DbType.DateTimeOffset:
                dbParam.Size = MaxDateTimeOffset;
                break;
        }
    }

    private static bool IsText(IDbDataParameter dbParam, SqlType sqlType)
    {
        return (sqlType is StringClobSqlType) || (sqlType.LengthDefined && sqlType.Length > MsSql2000Dialect.MaxSizeForLengthLimitedStrings &&
            (DbType.String == dbParam.DbType || DbType.StringFixedLength == dbParam.DbType));
    }

    private static void SetVariableLengthParameterSize(IDbDataParameter dbParam, SqlType sqlType)
    {
        SetDefaultParameterSize(dbParam, sqlType);

        // Override the defaults using data from SqlType.
        if (sqlType.LengthDefined && !IsText(dbParam, sqlType))
        {
            dbParam.Size = sqlType.Length;
        }

        if (sqlType.PrecisionDefined)
        {
            dbParam.Precision = sqlType.Precision;
            dbParam.Scale = sqlType.Scale;
        }
    }

    public override IDbCommand GenerateCommand(CommandType type, SqlString sqlString, SqlType[] parameterTypes)
    {
        IDbCommand command = base.GenerateCommand(type, sqlString, parameterTypes);
        //if (IsPrepareSqlEnabled)
        {
            SetParameterSizes(command.Parameters, parameterTypes);
        }
        return command;
    }

    public override bool SupportsMultipleQueries
    {
        get { return true; }
    }

    #region IEmbeddedBatcherFactoryProvider Members

    System.Type IEmbeddedBatcherFactoryProvider.BatcherFactoryClass
    {
        get { return typeof(ProfiledSqlClientBatchingBatcherFactory); }
    }

    #endregion
}


public class ProfiledSqlClientBatchingBatcher : AbstractBatcher
{
    private int batchSize;
    private int totalExpectedRowsAffected;
    private SqlClientSqlCommandSet currentBatch;
    private StringBuilder currentBatchCommandsLog;
    private readonly int defaultTimeout;

    public ProfiledSqlClientBatchingBatcher(ConnectionManager connectionManager, IInterceptor interceptor)
        : base(connectionManager, interceptor)
    {
        batchSize = Factory.Settings.AdoBatchSize;
        defaultTimeout = PropertiesHelper.GetInt32(NHibernate.Cfg.Environment.CommandTimeout, NHibernate.Cfg.Environment.Properties, -1);

        currentBatch = CreateConfiguredBatch();
        //we always create this, because we need to deal with a scenario in which
        //the user change the logging configuration at runtime. Trying to put this
        //behind an if(log.IsDebugEnabled) will cause a null reference exception 
        //at that point.
        currentBatchCommandsLog = new StringBuilder().AppendLine("Batch commands:");
    }

    public override int BatchSize
    {
        get { return batchSize; }
        set { batchSize = value; }
    }

    protected override int CountOfStatementsInCurrentBatch
    {
        get { return currentBatch.CountOfCommands; }
    }

    public override void AddToBatch(IExpectation expectation)
    {
        totalExpectedRowsAffected += expectation.ExpectedRowCount;
        IDbCommand batchUpdate = CurrentCommand;

        string lineWithParameters = null;
        var sqlStatementLogger = Factory.Settings.SqlStatementLogger;
        if (sqlStatementLogger.IsDebugEnabled || log.IsDebugEnabled)
        {
            lineWithParameters = sqlStatementLogger.GetCommandLineWithParameters(batchUpdate);
            var formatStyle = sqlStatementLogger.DetermineActualStyle(FormatStyle.Basic);
            lineWithParameters = formatStyle.Formatter.Format(lineWithParameters);
            currentBatchCommandsLog.Append("command ")
                .Append(currentBatch.CountOfCommands)
                .Append(":")
                .AppendLine(lineWithParameters);
        }
        if (log.IsDebugEnabled)
        {
            log.Debug("Adding to batch:" + lineWithParameters);
        }
        currentBatch.Append(((ProfiledSqlDbCommand)batchUpdate).Command);

        if (currentBatch.CountOfCommands >= batchSize)
        {
            ExecuteBatchWithTiming(batchUpdate);
        }
    }

    protected void ProfiledPrepare(IDbCommand cmd)
    {
        try
        {
            IDbConnection sessionConnection = ConnectionManager.GetConnection();

            if (cmd.Connection != null)
            {
                // make sure the commands connection is the same as the Sessions connection
                // these can be different when the session is disconnected and then reconnected
                if (cmd.Connection != sessionConnection)
                {
                    cmd.Connection = sessionConnection;
                }
            }
            else
            {
                cmd.Connection = (sessionConnection as ProfiledSqlDbConnection).Connection;
            }

            ProfiledSqlDbTransaction trans = (ProfiledSqlDbTransaction)typeof(NHibernate.Transaction.AdoTransaction).InvokeMember("trans", System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance, null, ConnectionManager.Transaction, null);
            if (trans != null)
                cmd.Transaction = trans.Transaction;
            Factory.ConnectionProvider.Driver.PrepareCommand(cmd);
        }
        catch (InvalidOperationException ioe)
        {
            throw new ADOException("While preparing " + cmd.CommandText + " an error occurred", ioe);
        }
    }

    protected override void DoExecuteBatch(IDbCommand ps)
    {
        log.DebugFormat("Executing batch");
        CheckReaders();
        ProfiledPrepare(currentBatch.BatchCommand);
        if (Factory.Settings.SqlStatementLogger.IsDebugEnabled)
        {
            Factory.Settings.SqlStatementLogger.LogBatchCommand(currentBatchCommandsLog.ToString());
            currentBatchCommandsLog = new StringBuilder().AppendLine("Batch commands:");
        }

        int rowsAffected;
        try
        {
            rowsAffected = currentBatch.ExecuteNonQuery();
        }
        catch (DbException e)
        {
            throw ADOExceptionHelper.Convert(Factory.SQLExceptionConverter, e, "could not execute batch command.");
        }

        Expectations.VerifyOutcomeBatched(totalExpectedRowsAffected, rowsAffected);

        currentBatch.Dispose();
        totalExpectedRowsAffected = 0;
        currentBatch = CreateConfiguredBatch();
    }

    private SqlClientSqlCommandSet CreateConfiguredBatch()
    {
        var result = new SqlClientSqlCommandSet();
        if (defaultTimeout > 0)
        {
            try
            {
                result.CommandTimeout = defaultTimeout;
            }
            catch (Exception e)
            {
                if (log.IsWarnEnabled)
                {
                    log.Warn(e.ToString());
                }
            }
        }

        return result;
    }
}


public class ProfiledSqlClientBatchingBatcherFactory : IBatcherFactory
{
    public virtual IBatcher CreateBatcher(ConnectionManager connectionManager, IInterceptor interceptor)
    {
        return new ProfiledSqlClientBatchingBatcher(connectionManager, interceptor);
    }
}


public class ProfiledSqlDbCommand : ProfiledDbCommand
{
    public ProfiledSqlDbCommand(SqlCommand cmd, SqlConnection conn, MiniProfiler profiler)
        : base(cmd, conn, profiler)
    {
        Command = cmd;
    }

    public SqlCommand Command { get; set; }

    private DbTransaction _trans;

    protected override DbTransaction DbTransaction
    {
        get { return _trans; }
        set
        {
            this._trans = value;
            ProfiledSqlDbTransaction awesomeTran = value as ProfiledSqlDbTransaction;
            Command.Transaction = awesomeTran == null ? (SqlTransaction)value : awesomeTran.Transaction;
        }
    }
}



public class ProfiledSqlDbConnection : ProfiledDbConnection
{
    public ProfiledSqlDbConnection(SqlConnection connection, MiniProfiler profiler)
        : base(connection, profiler)
    {
        Connection = connection;
    }

    public SqlConnection Connection { get; set; }

    protected override DbTransaction BeginDbTransaction(System.Data.IsolationLevel isolationLevel)
    {
        return new ProfiledSqlDbTransaction(Connection.BeginTransaction(isolationLevel), this);
    }       

}


public class ProfiledSqlDbTransaction : ProfiledDbTransaction
{
    public ProfiledSqlDbTransaction(SqlTransaction transaction, ProfiledDbConnection connection)
        : base(transaction, connection)
    {
        Transaction = transaction;
    }

    public SqlTransaction Transaction { get; set; }
}

3voto

Mauricio Scheffer Points 70470

Essayez d'adopter NHibernate.Connection.IConnectionProvider (vous pouvez simplement hériter DriverConnectionProvider), en GetConnection() envelopper l' IDbConnection que vous avez besoin.

Branchez votre fournisseur de connexion à l'aide de l' Environment.ConnectionProvider clé dans le fichier de propriétés.

1voto

Konstantin Points 1118

Si quelqu'un est intéressé, j'ai fait une intégration à l'aide d'un custom Log4net appender à la place. De cette façon, je me sens en sécurité, que je ne plaisante pas avec l'objet de Connexion.

L'ébauche est quelque chose le long de ces lignes: NHibernate émet le sqlstrings que des instructions de débogage et de l'appender configuré dans log4net.xml les appels de Début et de Disposer sur le MiniProfiler.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X