我们在使用第 3 方 Sybase 数据库时遇到了同样的问题。幸运的是,在深入研究了 NHibernate 代码并与开发人员进行了简短讨论之后,似乎有一个不需要更改 NHibernate 的简单解决方案。 Fabio Maulo 在this thread in the NHibernate developer group 中给出了解决方案。
为了为 Sybase 实现这一点,我们创建了自己的 IBatcherFactory 实现,继承自 NonBatchingBatcher 并覆盖 AddToBatch() 方法以删除对提供的 IExpectation 对象的 VerifyOutcomeNonBatched() 的调用:
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() 并从 Expectations 对象中删除对 VerifyOutcomeBatched() 的调用:
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。我知道有两种方法可以做到这一点:
- 在 adonet.factory_class 配置属性中提供 IBatcherFactory 实现的名称
- 创建实现 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 中,这是使用继承自 PersistenceConfiguration 的类中的 AdoNetBatchSize() 方法完成的(有关此示例,请参见上面的 SybaseConfiguration 类构造函数)。