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

Я использую nHibernate для обновления 2 столбцов в таблице, на которой есть три зашифрованных триггера. Триггеры не принадлежат мне, и я не могу вносить в них изменения, поэтому, к сожалению, я не могу установить NOCOUNT ON внутри них.

Есть ли другой способ обойти исключение TooManyRowsAffectedException, которое бросается на commit?

Обновление 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();

Он чувствует себя грязным и не просто растягивается, но он не кратер.

Ответы

Ответ 1

У нас была та же проблема с третьей базой данных 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. Есть два способа сделать это, о которых я знаю:

  • Укажите имя вашей реализации IBatcherFactory в свойстве конфигурации adonet.factory_class
  • Создайте собственный драйвер, реализующий интерфейс 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>();
        }
    }
}

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

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 для примера).

Ответ 2

er... вы можете в состоянии расшифровать их...

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

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

Ответ 3

Установка опции Disallow Results from Triggers для 1 работала для нас (по умолчанию 0).

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