0

NHibernate の SqlClientBatchingBatcher クラスを次のように継承したいと考えています ( 暗号化されたトリガーを使用して TooManyRowsAffectedExceptionから取得したコード):

public class NonBatchingBatcherWithoutVerification : SqlClientBatchingBatcher
{
    public NonBatchingBatcherWithoutVerification(ConnectionManager connectionManager, IInterceptor interceptor) : base(connectionManager, interceptor)
    {}

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

        int rowsAffected = currentBatch.ExecuteNonQuery();

        // Removed the following line
        //Expectations.VerifyOutcomeBatched(totalExpectedRowsAffected, rowsAffected);

        currentBatch.Dispose();
        totalExpectedRowsAffected = 0;
        currentBatch = new SqlClientSqlCommandSet();
    }
}

このメソッドでアクセスされるメンバーの一部 (currentBatch や totalExpectedRowsAffected など) に注目してください。

これらのメンバーは、現在の NHibernate 3.3 ソースのスーパークラスでは実際には非公開であることがわかりました。では、全体をコピーせずにクラスを効果的に継承するにはどうすればよいでしょうか。ちなみに、これはクラスの変更されていない NHibernate コードです。

 public class SqlClientBatchingBatcher : AbstractBatcher
{
    private int _batchSize;
    private int _totalExpectedRowsAffected;
    private SqlClientSqlCommandSet _currentBatch;
    private StringBuilder _currentBatchCommandsLog;
    private readonly int _defaultTimeout;

    public SqlClientBatchingBatcher(ConnectionManager connectionManager, IInterceptor interceptor)
        : base(connectionManager, interceptor)
    {
        _batchSize = Factory.Settings.AdoBatchSize;
        _defaultTimeout = PropertiesHelper.GetInt32(Cfg.Environment.CommandTimeout, 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;
        Driver.AdjustCommand(batchUpdate);
        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((System.Data.SqlClient.SqlCommand) batchUpdate);

        if (_currentBatch.CountOfCommands >= _batchSize)
        {
            ExecuteBatchWithTiming(batchUpdate);
        }
    }

    protected override void DoExecuteBatch(IDbCommand ps)
    {
        Log.DebugFormat("Executing batch");
        CheckReaders();
        Prepare(_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;
    }
}

私は何かを見落としましたか?プライベート メンバーへのすべてのアクセスをオーバーライドするためだけに全体をコピーするのは、かなり悪いアプローチのようです。1 つのメソッドをオーバーライドしたいだけです。

4

4 に答える 4

11

基底クラスのプライベート メンバーに合法的にアクセスする方法は 1 つしかありません。派生クラスを基底クラス内に配置します。

class Base
{
    private int x;
    private class Derived : Base
    {
        private void M()
        {
            Console.WriteLine(this.x); // legal!
        }
    }
}

もちろん、クラスを基本クラス内に置くことができれば、メンバーが保護されるように基本クラスを書き直すこともできます。

元の作成者がメンバーを非公開にしたということは、クラスがそのデータをいじくり回せるように設計されていないことを示しています。

于 2013-03-20T21:10:50.723 に答える
4

それらが非公開として設定されている場合、実際にできることは何もありません (リフレクションを使用する以外には、醜く、常に安全であるとは限りません)。

于 2013-03-20T20:48:15.803 に答える
1

Privateスーパークラスのメンバーにはアクセスできませんprivate。OOP のカプセル化は、この直接アクセスを禁止しオブジェクトが適切に機能するようにするために存在します。
プライベート メンバーにアクセスする場合がありpropertiesます。これらは、プライベート メンバーからの読み取り/書き込みに使用できるものです。プロパティは、オブジェクトへの害が行われないことを保証します。

于 2013-03-20T20:50:11.300 に答える
1

リフレクションを使用して、親クラスのプライベート フィールド、プロパティ、およびメソッドにアクセスできます (たとえば、ここで説明されているようにフィールドにアクセスします:ベース クラスからプライベート フィールドを反映する) 。

これは安全ではありませんが、プライベートの考え方は、ライブラリの実装が変更される可能性があり、それらのプライベート メソッド、フィールド、およびプロパティが変更または消失する可能性があるためです。実装が変更された場合、更新によってコードが破損する可能性があります。

そうは言っても、私はそれを自分で数回やったことがあります。リスクを量る必要があるだけです。

于 2013-03-20T20:51:01.447 に答える