TooManyRowsAffectedException с зашифрованными триггерами

Я использую nHibernate для обновления 2 столбцов в таблице с 3 зашифрованными триггерами. Триггеры не принадлежат мне, и я не могу вносить в них изменения, поэтому, к сожалению, я не могу ВКЛЮЧИТЬ NOCOUNT внутри них.

Есть ли другой способ обойти исключение TooManyRowsAffectedException, которое возникает при фиксации?

Обновление 1

Пока что единственный способ обойти эту проблему - обойти процедуру .Save с помощью

var query = session.CreateSQLQuery("update Orders set Notes = :Notes, Status = :Status where OrderId = :Order");
query.SetString("Notes", orderHeader.Notes);
query.SetString("Status", orderHeader.OrderStatus);
query.SetInt32("Order", orderHeader.OrderHeaderId);
query.ExecuteUpdate();

Он кажется грязным, его нелегко раздвинуть, но на нем нет воронок.


person Jon Masters    schedule 30.08.2009    source источник


Ответы (3)


У нас была такая же проблема со сторонней базой данных Sybase. К счастью, после некоторого изучения кода NHibernate и краткого обсуждения с разработчиками кажется, что существует простое решение, которое не требует изменений в NHibernate. Решение предоставлено Фабио Мауло в этой ветке в группе разработчиков NHibernate .

Чтобы реализовать это для Sybase, мы создали нашу собственную реализацию IBatcherFactory, унаследованную от NonBatchingBatcher, и переопределили метод AddToBatch (), чтобы удалить вызов VerifyOutcomeNonBatched () в предоставленном объекте IExpectation:

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

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

    public override void AddToBatch(IExpectation expectation)
    {
        IDbCommand cmd = CurrentCommand;
        ExecuteNonQuery(cmd);
        // Removed the following line
        //expectation.VerifyOutcomeNonBatched(rowCount, cmd);
    }
}

Чтобы сделать то же самое для SQL Server, вам необходимо унаследовать от SqlClientBatchingBatcher, переопределить DoExectuteBatch () и удалить вызов VerifyOutcomeBatched () из объекта Expectations:

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();
    }
}

Теперь вам нужно внедрить свои новые классы в NHibernate. Я знаю два способа сделать это:

  1. Укажите имя вашей реализации IBatcherFactory в свойстве конфигурации adonet.factory_class
  2. Создайте собственный драйвер, реализующий интерфейс IEmbeddedBatcherFactoryProvider.

Учитывая, что в нашем проекте уже был специальный драйвер для решения проблем со строками Sybase 12 ANSI, было несложно реализовать интерфейс следующим образом:

public class DriverWithCustomBatcherFactory : SybaseAdoNet12ClientDriver, IEmbeddedBatcherFactoryProvider
{
    public Type BatcherFactoryClass
    {
        get { return typeof(NonVerifyingBatcherFactory); }
    }

    //...other driver code for our project...
}

Драйвер можно настроить, указав имя драйвера с помощью свойства конфигурации connection.driver_class. Мы хотели использовать Fluent NHibernate, и это можно сделать с помощью Fluent следующим образом:

public class SybaseConfiguration : PersistenceConfiguration<SybaseConfiguration, SybaseConnectionStringBuilder>
{
    SybaseConfiguration()
    {
        Driver<DriverWithCustomBatcherFactory>();
        AdoNetBatchSize(1); // This is required to use our new batcher
    }

    /// <summary>
    /// The dialect to use
    /// </summary>
    public static SybaseConfiguration SybaseDialect
    {
        get
        {
            return new SybaseConfiguration()
                .Dialect<SybaseAdoNet12Dialect>();
        }
    }
}

и при создании фабрики сеансов мы используем этот новый класс следующим образом:

var sf = Fluently.Configure()
    .Database(SybaseConfiguration.SybaseDialect.ConnectionString(_connectionString))
    .Mappings(m => m.FluentMappings.AddFromAssemblyOf<MyEntity>())
    .BuildSessionFactory();

Наконец, вам нужно установить для свойства adonet.batch_size значение 1, чтобы гарантировать использование вашего нового класса дозатора. В Fluent NHibernate это делается с помощью метода AdoNetBatchSize () в классе, который наследуется от PersistenceConfiguration (см. Выше пример конструктора класса SybaseConfiguration).

person MikeD    schedule 23.04.2010
comment
У меня такая же проблема (с использованием Sql Server). Однако в этом случае мы не используем пакетную обработку, поэтому исключение TooManyRowsAffectedException выбрасывается из метода VerifyOutcomeNonBatched, который вызывается AbstractEntityPersister.UpdateOrInsert (через методы Update и Check). Приходилось ли вам учитывать этот сценарий в своем решении? - person Andy McCluggage; 06.12.2010
comment
@andy Мне неизвестна точка расширения, позволяющая переопределить не пакетную проверку, но я не являюсь экспертом по кодовой базе NHibernate. Когда я задал свой первоначальный вопрос в списке рассылки NH, Фабио изложил решение с использованием фабрики дозаторов, и поэтому я переключился на использование партий. В результате внесения этого изменения у меня не возникло никаких проблем. - person MikeD; 07.12.2010
comment
Спасибо @MikeD. Переход на пакетную обработку, чтобы я мог использовать это решение, вполне может быть для меня вариантом. Включает ли включение пакетной обработки в конфигурацию нечто большее, чем добавление свойства фабрики сеанса adonet.batch_size? - person Andy McCluggage; 07.12.2010
comment
@MikeD Большое спасибо за это, я только что реализовал это на SQL Server. Однако мне пришлось внести серьезные изменения, чтобы заставить его работать. Я не знаю, изменился ли NHibernate с тех пор, как вы это сделали или что. Вы не возражаете, если я отредактирую ваш ответ, чтобы отразить изменения? (Я просто добавлю раздел для SQL Server, а остальное оставлю в покое) - person Matthew Talbert; 10.09.2011
comment
@MatthewTalbert Я знаю, что NHibernate многое изменил в последней версии. Пожалуйста, не стесняйтесь редактировать. Я бы хотел, чтобы этот ответ продолжал помогать людям. - person MikeD; 20.09.2011
comment
@MikeD Спасибо, это действительно помогло мне. Используя SQL Server, мне пришлось скопировать весь SqlClientBatchingBatcher, поскольку некоторые поля, к которым осуществляется доступ из DoExecuteBatch(), являются частными. Кроме того, он работал отлично (очевидно, с использованием правильных типов для SQL Server) - person Richard Dalton; 10.10.2011

э ... возможно, вы сможете их расшифровать ...

Изменить: если вы не можете изменить код, расшифровать или отключить, тогда у вас нет параметров кода на стороне SQL Server.

Однако вы можете попробовать «запретить результаты от триггеров Option ", что подходит для SQL 2005 и SQL 2008, но будет удалено в более поздних версиях. Я не знаю, подавляет ли он сообщения о количестве строк.

person gbn    schedule 30.08.2009
comment
К сожалению, взлом шифрования не является жизнеспособным решением. - person Jon Masters; 30.08.2009

Установка параметра «Запретить результаты по триггерам» на 1 сработала для нас ( по умолчанию 0).

Обратите внимание, что этот параметр не будет доступен в будущих выпусках Microsoft SQL Server, но после того, как он больше не будет доступен, он будет вести себя так, как если бы он был установлен на 1. Таким образом, установка этого параметра на 1 теперь устраняет проблему, а также дает вам то же самое. поведение, которое будет в будущих выпусках.

person TylerOhlsen    schedule 02.05.2012