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