From 78da6373e3279089c6d609a2005f3bc843d70f5b Mon Sep 17 00:00:00 2001 From: Gunnar Liljas Date: Thu, 14 Mar 2024 22:12:46 +0100 Subject: [PATCH 01/12] Add support for DbBatch --- src/NHibernate.Test/Ado/BatcherFixture.cs | 30 +- .../Async/Ado/BatcherFixture.cs | 30 +- src/NHibernate.Test/NHibernate.Test.csproj | 2 +- src/NHibernate/AdoNet/ConnectionManager.cs | 24 ++ src/NHibernate/AdoNet/DbBatchBatcher.cs | 297 ++++++++++++++++++ .../AdoNet/DbBatchBatcherFactory.cs | 14 + .../Async/Transaction/AdoTransaction.cs | 2 + ...erFactoryDriveConnectionCommandProvider.cs | 13 +- src/NHibernate/Driver/DriverBase.cs | 48 +++ ...nnectionCommandProviderWithBatchSupport.cs | 15 + src/NHibernate/Driver/IDriver.cs | 2 + .../Driver/IDriverWithBatchSupport.cs | 16 + .../Driver/ReflectionBasedDriver.cs | 33 +- ...eflectionDriveConnectionCommandProvider.cs | 32 +- src/NHibernate/ITransaction.cs | 16 +- src/NHibernate/Transaction/AdoTransaction.cs | 55 ++++ 16 files changed, 616 insertions(+), 13 deletions(-) create mode 100644 src/NHibernate/AdoNet/DbBatchBatcher.cs create mode 100644 src/NHibernate/AdoNet/DbBatchBatcherFactory.cs create mode 100644 src/NHibernate/Driver/IDriveConnectionCommandProviderWithBatchSupport.cs create mode 100644 src/NHibernate/Driver/IDriverWithBatchSupport.cs diff --git a/src/NHibernate.Test/Ado/BatcherFixture.cs b/src/NHibernate.Test/Ado/BatcherFixture.cs index df7a68d452c..b54a0a73e67 100644 --- a/src/NHibernate.Test/Ado/BatcherFixture.cs +++ b/src/NHibernate.Test/Ado/BatcherFixture.cs @@ -4,9 +4,18 @@ namespace NHibernate.Test.Ado { - [TestFixture] +#if NET6_0_OR_GREATER + [TestFixture(true)] +#endif + [TestFixture(false)] public class BatcherFixture: TestCase { + private readonly bool _useDbBatch; + + public BatcherFixture(bool useDbBatch) + { + _useDbBatch = useDbBatch; + } protected override string MappingsAssembly { get { return "NHibernate.Test"; } @@ -22,10 +31,22 @@ protected override void Configure(Configuration configuration) configuration.SetProperty(Environment.FormatSql, "true"); configuration.SetProperty(Environment.GenerateStatistics, "true"); configuration.SetProperty(Environment.BatchSize, "10"); + #if NET6_0_OR_GREATER + if (_useDbBatch) + { + configuration.SetProperty(Environment.BatchStrategy, typeof(DbBatchBatcherFactory).AssemblyQualifiedName); + } + #endif } protected override bool AppliesTo(Engine.ISessionFactoryImplementor factory) { +#if NET6_0_OR_GREATER + if (_useDbBatch) + { + return factory.Settings.BatcherFactory is DbBatchBatcherFactory && factory.Settings.ConnectionProvider.Driver is Driver.DriverBase driverBase && driverBase.CanCreateBatch; + } +#endif return !(factory.Settings.BatcherFactory is NonBatchingBatcherFactory); } @@ -129,13 +150,18 @@ public void SqlClientOneRoundTripForUpdateAndInsert() Cleanup(); } - [Test, NetFxOnly] + [Test] [Description("SqlClient: The batcher log output should be formatted")] public void BatchedoutputShouldBeFormatted() { #if NETFX if (Sfi.Settings.BatcherFactory is SqlClientBatchingBatcherFactory == false) Assert.Ignore("This test is for SqlClientBatchingBatcher only"); +#elif NET6_0_OR_GREATER + if (Sfi.Settings.BatcherFactory is DbBatchBatcherFactory == false) + Assert.Ignore("This test is for DbBatchBatcherFactory only"); +#else + Assert.Ignore("This test is for NETFX and NET6_0_OR_GREATER only"); #endif using (var sqlLog = new SqlLogSpy()) diff --git a/src/NHibernate.Test/Async/Ado/BatcherFixture.cs b/src/NHibernate.Test/Async/Ado/BatcherFixture.cs index 17dfbcbeaa9..6ccd4f30a72 100644 --- a/src/NHibernate.Test/Async/Ado/BatcherFixture.cs +++ b/src/NHibernate.Test/Async/Ado/BatcherFixture.cs @@ -16,9 +16,18 @@ namespace NHibernate.Test.Ado { using System.Threading.Tasks; using System.Threading; - [TestFixture] +#if NET6_0_OR_GREATER + [TestFixture(true)] +#endif + [TestFixture(false)] public class BatcherFixtureAsync: TestCase { + private readonly bool _useDbBatch; + + public BatcherFixtureAsync(bool useDbBatch) + { + _useDbBatch = useDbBatch; + } protected override string MappingsAssembly { get { return "NHibernate.Test"; } @@ -34,10 +43,22 @@ protected override void Configure(Configuration configuration) configuration.SetProperty(Environment.FormatSql, "true"); configuration.SetProperty(Environment.GenerateStatistics, "true"); configuration.SetProperty(Environment.BatchSize, "10"); + #if NET6_0_OR_GREATER + if (_useDbBatch) + { + configuration.SetProperty(Environment.BatchStrategy, typeof(DbBatchBatcherFactory).AssemblyQualifiedName); + } + #endif } protected override bool AppliesTo(Engine.ISessionFactoryImplementor factory) { +#if NET6_0_OR_GREATER + if (_useDbBatch) + { + return factory.Settings.BatcherFactory is DbBatchBatcherFactory && factory.Settings.ConnectionProvider.Driver is Driver.DriverBase driverBase && driverBase.CanCreateBatch; + } +#endif return !(factory.Settings.BatcherFactory is NonBatchingBatcherFactory); } @@ -101,13 +122,18 @@ public async Task OneRoundTripUpdateAsync() await (CleanupAsync()); } - [Test, NetFxOnly] + [Test] [Description("SqlClient: The batcher log output should be formatted")] public async Task BatchedoutputShouldBeFormattedAsync() { #if NETFX if (Sfi.Settings.BatcherFactory is SqlClientBatchingBatcherFactory == false) Assert.Ignore("This test is for SqlClientBatchingBatcher only"); +#elif NET6_0_OR_GREATER + if (Sfi.Settings.BatcherFactory is DbBatchBatcherFactory == false) + Assert.Ignore("This test is for DbBatchBatcherFactory only"); +#else + Assert.Ignore("This test is for NETFX and NET6_0_OR_GREATER only"); #endif using (var sqlLog = new SqlLogSpy()) diff --git a/src/NHibernate.Test/NHibernate.Test.csproj b/src/NHibernate.Test/NHibernate.Test.csproj index 5bae7bee13b..62470068782 100644 --- a/src/NHibernate.Test/NHibernate.Test.csproj +++ b/src/NHibernate.Test/NHibernate.Test.csproj @@ -64,7 +64,7 @@ - + diff --git a/src/NHibernate/AdoNet/ConnectionManager.cs b/src/NHibernate/AdoNet/ConnectionManager.cs index 882452b4d98..e258e5b9232 100644 --- a/src/NHibernate/AdoNet/ConnectionManager.cs +++ b/src/NHibernate/AdoNet/ConnectionManager.cs @@ -514,6 +514,30 @@ public void EnlistInTransaction(DbCommand command) } } +#if NET6_0_OR_GREATER + /// + /// Enlist a batch in the current transaction, if any. + /// + /// The batch to enlist. + public void EnlistInTransaction(DbBatch batch) + { + if (batch == null) + throw new ArgumentNullException(nameof(batch)); + + if (_transaction is ITransactionWithBatchSupport transactionWithBatch) + { + transactionWithBatch.Enlist(batch); + return; + } + + if (batch.Transaction != null) + { + _log.Warn("set a nonnull DbCommand.Transaction to null because the Session had no Transaction"); + batch.Transaction = null; + } + } +#endif + /// /// Enlist the connection into provided transaction if the connection should be enlisted. /// Do nothing in case an explicit transaction is ongoing. diff --git a/src/NHibernate/AdoNet/DbBatchBatcher.cs b/src/NHibernate/AdoNet/DbBatchBatcher.cs new file mode 100644 index 00000000000..1bfb9703262 --- /dev/null +++ b/src/NHibernate/AdoNet/DbBatchBatcher.cs @@ -0,0 +1,297 @@ +#if NET6_0_OR_GREATER +using System; +using System.Data.Common; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using NHibernate.AdoNet.Util; +using NHibernate.Driver; +using NHibernate.Engine; +using NHibernate.Exceptions; + +namespace NHibernate.AdoNet +{ + public class DbBatchBatcher : AbstractBatcher + { + private int _batchSize; + private int _totalExpectedRowsAffected; + private DbBatch _currentBatch; + private StringBuilder _currentBatchCommandsLog; + private readonly int _defaultTimeout; + private readonly ConnectionManager _connectionManager; + + public DbBatchBatcher(ConnectionManager connectionManager, IInterceptor interceptor) + : base(connectionManager, interceptor) + { + _batchSize = Factory.Settings.AdoBatchSize; + _defaultTimeout = Driver.GetCommandTimeout(); + + _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:"); + _connectionManager = connectionManager; + } + + public override int BatchSize + { + get { return _batchSize; } + set { _batchSize = value; } + } + + protected override int CountOfStatementsInCurrentBatch + { + get { return _currentBatch.BatchCommands.Count; } + } + + public override void AddToBatch(IExpectation expectation) + { + _totalExpectedRowsAffected += expectation.ExpectedRowCount; + var 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.BatchCommands.Count) + .Append(":") + .AppendLine(lineWithParameters); + } + if (Log.IsDebugEnabled()) + { + Log.Debug("Adding to batch:{0}", lineWithParameters); + } + + AddCommandToBatch(batchUpdate); + + if (CountOfStatementsInCurrentBatch >= _batchSize) + { + ExecuteBatchWithTiming(batchUpdate); + } + } + + private void AddCommandToBatch(DbCommand batchUpdate) + { + var dbBatchCommand = _currentBatch.CreateBatchCommand(); + dbBatchCommand.CommandText = batchUpdate.CommandText; + dbBatchCommand.CommandType = batchUpdate.CommandType; + + foreach (var param in batchUpdate.Parameters) + { + dbBatchCommand.Parameters.Add(((ICloneable) param).Clone()); + } + _currentBatch.BatchCommands.Add(dbBatchCommand); + } + + public override Task AddToBatchAsync(IExpectation expectation, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + try + { + _totalExpectedRowsAffected += expectation.ExpectedRowCount; + var 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.BatchCommands.Count) + .Append(":") + .AppendLine(lineWithParameters); + } + if (Log.IsDebugEnabled()) + { + Log.Debug("Adding to batch:{0}", lineWithParameters); + } + + AddCommandToBatch(batchUpdate); + + if (CountOfStatementsInCurrentBatch >= _batchSize) + { + return ExecuteBatchWithTimingAsync(batchUpdate, cancellationToken); + } + return Task.CompletedTask; + } + catch (Exception ex) + { + return Task.FromException(ex); + } + } + + protected override void DoExecuteBatch(DbCommand ps) + { + try + { + Log.Debug("Executing batch"); + CheckReaders(); + Prepare(_currentBatch); + if (Factory.Settings.SqlStatementLogger.IsDebugEnabled) + { + Factory.Settings.SqlStatementLogger.LogBatchCommand(_currentBatchCommandsLog.ToString()); + } + int rowsAffected; + try + { + rowsAffected = _currentBatch.ExecuteNonQuery(); + } + catch (DbException e) + { + throw ADOExceptionHelper.Convert(Factory.SQLExceptionConverter, e, "could not execute batch command."); + } + + Expectations.VerifyOutcomeBatched(_totalExpectedRowsAffected, rowsAffected, ps); + } + finally + { + ClearCurrentBatch(); + } + } + + protected override async Task DoExecuteBatchAsync(DbCommand ps, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + try + { + Log.Debug("Executing batch"); + await (CheckReadersAsync(cancellationToken)).ConfigureAwait(false); + //await (PrepareAsync(_currentBatch, cancellationToken)).ConfigureAwait(false); + Prepare(_currentBatch); + if (Factory.Settings.SqlStatementLogger.IsDebugEnabled) + { + Factory.Settings.SqlStatementLogger.LogBatchCommand(_currentBatchCommandsLog.ToString()); + } + int rowsAffected; + try + { + rowsAffected = _currentBatch.ExecuteNonQuery(); + } + catch (DbException e) + { + throw ADOExceptionHelper.Convert(Factory.SQLExceptionConverter, e, "could not execute batch command."); + } + + Expectations.VerifyOutcomeBatched(_totalExpectedRowsAffected, rowsAffected, ps); + } + finally + { + ClearCurrentBatch(); + } + } + + private DbBatch CreateConfiguredBatch() + { + var result = ((IDriverWithBatchSupport) Driver).CreateBatch(); + if (_defaultTimeout > 0) + { + try + { + result.Timeout = _defaultTimeout; + } + catch (Exception e) + { + if (Log.IsWarnEnabled()) + { + Log.Warn(e, e.ToString()); + } + } + } + + return result; + } + + private void ClearCurrentBatch() + { + _currentBatch.Dispose(); + _totalExpectedRowsAffected = 0; + _currentBatch = CreateConfiguredBatch(); + + if (Factory.Settings.SqlStatementLogger.IsDebugEnabled) + { + _currentBatchCommandsLog = new StringBuilder().AppendLine("Batch commands:"); + } + } + + public override void CloseCommands() + { + base.CloseCommands(); + + // Prevent exceptions when closing the batch from hiding any original exception + // (We do not know here if this batch closing occurs after a failure or not.) + try + { + ClearCurrentBatch(); + } + catch (Exception e) + { + Log.Warn(e, "Exception clearing batch"); + } + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + // Prevent exceptions when closing the batch from hiding any original exception + // (We do not know here if this batch closing occurs after a failure or not.) + try + { + _currentBatch.Dispose(); + } + catch (Exception e) + { + Log.Warn(e, "Exception closing batcher"); + } + } + + /// + /// Prepares the for execution in the database. + /// + /// + /// This takes care of hooking the up to an + /// and if one exists. It will call Prepare if the Driver + /// supports preparing batches. + /// + protected void Prepare(DbBatch batch) + { + try + { + var sessionConnection = _connectionManager.GetConnection(); + + if (batch.Connection != null) + { + // make sure the commands connection is the same as the Sessions connection + // these can be different when the session is disconnected and then reconnected + if (batch.Connection != sessionConnection) + { + batch.Connection = sessionConnection; + } + } + else + { + batch.Connection = sessionConnection; + } + + _connectionManager.EnlistInTransaction(batch); + (Driver as IDriverWithBatchSupport).PrepareBatch(batch); + } + catch (InvalidOperationException ioe) + { + throw new ADOException("While preparing " + string.Join(Environment.NewLine, batch.BatchCommands.Select(x => x.CommandText)) + " an error occurred", ioe); + } + } + } +} +#endif diff --git a/src/NHibernate/AdoNet/DbBatchBatcherFactory.cs b/src/NHibernate/AdoNet/DbBatchBatcherFactory.cs new file mode 100644 index 00000000000..c6928c106f8 --- /dev/null +++ b/src/NHibernate/AdoNet/DbBatchBatcherFactory.cs @@ -0,0 +1,14 @@ +#if NET6_0_OR_GREATER +using NHibernate.Engine; + +namespace NHibernate.AdoNet +{ + public class DbBatchBatcherFactory : IBatcherFactory + { + public IBatcher CreateBatcher(ConnectionManager connectionManager, IInterceptor interceptor) + { + return new DbBatchBatcher(connectionManager, interceptor); + } + } +} +#endif diff --git a/src/NHibernate/Async/Transaction/AdoTransaction.cs b/src/NHibernate/Async/Transaction/AdoTransaction.cs index a8b249130fa..e1f7497fab6 100644 --- a/src/NHibernate/Async/Transaction/AdoTransaction.cs +++ b/src/NHibernate/Async/Transaction/AdoTransaction.cs @@ -21,6 +21,8 @@ namespace NHibernate.Transaction using System.Threading.Tasks; using System.Threading; public partial class AdoTransaction : ITransaction +#if NET6_0_OR_GREATER +#endif { private async Task AfterTransactionCompletionAsync(bool successful, CancellationToken cancellationToken) diff --git a/src/NHibernate/Driver/DbProviderFactoryDriveConnectionCommandProvider.cs b/src/NHibernate/Driver/DbProviderFactoryDriveConnectionCommandProvider.cs index 3c3a36db553..6467983357a 100644 --- a/src/NHibernate/Driver/DbProviderFactoryDriveConnectionCommandProvider.cs +++ b/src/NHibernate/Driver/DbProviderFactoryDriveConnectionCommandProvider.cs @@ -4,6 +4,9 @@ namespace NHibernate.Driver { public class DbProviderFactoryDriveConnectionCommandProvider : IDriveConnectionCommandProvider +#if NET6_0_OR_GREATER + , IDriveConnectionCommandProviderWithBatchSupport +#endif { private readonly DbProviderFactory dbProviderFactory; @@ -25,5 +28,13 @@ public DbCommand CreateCommand() { return dbProviderFactory.CreateCommand(); } +#if NET6_0_OR_GREATER + public DbBatch CreateBatch() + { + return dbProviderFactory.CreateBatch(); + } + + public bool CanCreateBatch => dbProviderFactory.CanCreateBatch && dbProviderFactory.CreateCommand() is ICloneable; +#endif } -} \ No newline at end of file +} diff --git a/src/NHibernate/Driver/DriverBase.cs b/src/NHibernate/Driver/DriverBase.cs index be5c81366e1..4b692af6347 100644 --- a/src/NHibernate/Driver/DriverBase.cs +++ b/src/NHibernate/Driver/DriverBase.cs @@ -15,6 +15,9 @@ namespace NHibernate.Driver /// Base class for the implementation of IDriver /// public abstract class DriverBase : IDriver, ISqlParameterFormatter +#if NET6_0_OR_GREATER + , IDriverWithBatchSupport +#endif { private static readonly INHibernateLogger log = NHibernateLogger.For(typeof(DriverBase)); @@ -329,6 +332,51 @@ public virtual void AdjustCommand(DbCommand command) { } +#if NET6_0_OR_GREATER + public void PrepareBatch(DbBatch batch) + { + AdjustBatch(batch); + OnBeforePrepare(batch); + + if (SupportsPreparingCommands && prepareSql) + { + batch.Prepare(); + } + } + + /// + /// Override to make any adjustments to the DbBatch object. (e.g., Oracle custom OUT parameter) + /// Parameters have been bound by this point, so their order can be adjusted too. + /// This is analogous to the RegisterResultSetOutParameter() function in Hibernate. + /// + protected virtual void OnBeforePrepare(DbBatch command) + { + } + + /// + /// Override to make any adjustments to each DbBatch object before it added to the batcher. + /// + /// The batch. + /// + /// This method is similar to the but, instead be called just before execute the command (that can be a batch) + /// is executed before add each single command to the batcher and before . + /// If you have to adjust parameters values/type (when the command is full filled) this is a good place where do it. + /// + public virtual void AdjustBatch(DbBatch batch) + { + + } + + public virtual DbBatch CreateBatch() + { + throw new NotImplementedException(); + } + + public virtual bool CanCreateBatch => false; + + +#endif + public DbParameter GenerateOutputParameter(DbCommand command) { var param = GenerateParameter(command, "ReturnValue", SqlTypeFactory.Int32); diff --git a/src/NHibernate/Driver/IDriveConnectionCommandProviderWithBatchSupport.cs b/src/NHibernate/Driver/IDriveConnectionCommandProviderWithBatchSupport.cs new file mode 100644 index 00000000000..dd9235de56b --- /dev/null +++ b/src/NHibernate/Driver/IDriveConnectionCommandProviderWithBatchSupport.cs @@ -0,0 +1,15 @@ +using System.Data.Common; + +namespace NHibernate.Driver +{ +#if NET6_0_OR_GREATER + //TODO: Include in IDriveConnectionCommandProvider for NET6_0_OR_GREATER + internal interface IDriveConnectionCommandProviderWithBatchSupport : IDriveConnectionCommandProvider + { + DbBatch CreateBatch(); + bool CanCreateBatch { get; } + + } +#endif + +} diff --git a/src/NHibernate/Driver/IDriver.cs b/src/NHibernate/Driver/IDriver.cs index 61e40b64213..2e723286635 100644 --- a/src/NHibernate/Driver/IDriver.cs +++ b/src/NHibernate/Driver/IDriver.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Data; using System.Data.Common; +using System.Linq; +using System.Windows.Input; using NHibernate.Engine; using NHibernate.SqlCommand; using NHibernate.SqlTypes; diff --git a/src/NHibernate/Driver/IDriverWithBatchSupport.cs b/src/NHibernate/Driver/IDriverWithBatchSupport.cs new file mode 100644 index 00000000000..60e5236906f --- /dev/null +++ b/src/NHibernate/Driver/IDriverWithBatchSupport.cs @@ -0,0 +1,16 @@ +using System.Data.Common; + +namespace NHibernate.Driver +{ +#if NET6_0_OR_GREATER + //TODO: Include in IDriver for NET6_0_OR_GREATER + internal interface IDriverWithBatchSupport : IDriver + { + public DbBatch CreateBatch(); + public bool CanCreateBatch{ get; } + + public void AdjustBatch(DbBatch dbBatch); + public void PrepareBatch(DbBatch dbBatch); + } +#endif +} diff --git a/src/NHibernate/Driver/ReflectionBasedDriver.cs b/src/NHibernate/Driver/ReflectionBasedDriver.cs index b8108102826..0ecd482e73e 100644 --- a/src/NHibernate/Driver/ReflectionBasedDriver.cs +++ b/src/NHibernate/Driver/ReflectionBasedDriver.cs @@ -7,16 +7,16 @@ namespace NHibernate.Driver public abstract class ReflectionBasedDriver : DriverBase { protected const string ReflectionTypedProviderExceptionMessageTemplate = "The DbCommand and DbConnection implementation in the assembly {0} could not be found. " - + "Ensure that the assembly {0} is located in the application directory or in the Global " - + "Assembly Cache. If the assembly is in the GAC, use element in the " - + "application configuration file to specify the full name of the assembly."; + + "Ensure that the assembly {0} is located in the application directory or in the Global " + + "Assembly Cache. If the assembly is in the GAC, use element in the " + + "application configuration file to specify the full name of the assembly."; private readonly IDriveConnectionCommandProvider connectionCommandProvider; /// /// If the driver use a third party driver (not a .Net Framework DbProvider), its assembly version. /// - protected Version DriverVersion { get; } + protected Version DriverVersion { get; } /// /// Initializes a new instance of with @@ -50,7 +50,7 @@ protected ReflectionBasedDriver(string providerInvariantName, string driverAssem if (string.IsNullOrEmpty(providerInvariantName)) { #endif - throw new HibernateException(string.Format(ReflectionTypedProviderExceptionMessageTemplate, driverAssemblyName)); + throw new HibernateException(string.Format(ReflectionTypedProviderExceptionMessageTemplate, driverAssemblyName)); #if NETFX || NETSTANDARD2_1_OR_GREATER } var factory = DbProviderFactories.GetFactory(providerInvariantName); @@ -73,5 +73,28 @@ public override DbCommand CreateCommand() { return connectionCommandProvider.CreateCommand(); } + +#if NET6_0_OR_GREATER + public override DbBatch CreateBatch() + { + if (connectionCommandProvider is IDriveConnectionCommandProviderWithBatchSupport driveConnectionCommandProviderWithBatch) + { + return driveConnectionCommandProviderWithBatch.CreateBatch(); + } + throw new NotSupportedException(); + } + + public override bool CanCreateBatch + { + get + { + if (connectionCommandProvider is IDriveConnectionCommandProviderWithBatchSupport driveConnectionCommandProviderWithBatch) + { + return driveConnectionCommandProviderWithBatch.CanCreateBatch; + } + return false; + } + } +#endif } } diff --git a/src/NHibernate/Driver/ReflectionDriveConnectionCommandProvider.cs b/src/NHibernate/Driver/ReflectionDriveConnectionCommandProvider.cs index 4d007ba96fb..071443498a7 100644 --- a/src/NHibernate/Driver/ReflectionDriveConnectionCommandProvider.cs +++ b/src/NHibernate/Driver/ReflectionDriveConnectionCommandProvider.cs @@ -5,6 +5,9 @@ namespace NHibernate.Driver { public class ReflectionDriveConnectionCommandProvider : IDriveConnectionCommandProvider +#if NET6_0_OR_GREATER + , IDriveConnectionCommandProviderWithBatchSupport +#endif { private readonly System.Type commandType; private readonly System.Type connectionType; @@ -21,6 +24,14 @@ public ReflectionDriveConnectionCommandProvider(System.Type connectionType, Syst } this.connectionType = connectionType; this.commandType = commandType; +#if NET6_0_OR_GREATER + _canCreateBatch = new Lazy(() => { + using (var connection = CreateConnection()) + { + return connection.CanCreateBatch && connection.CreateCommand() is ICloneable; + } + }); +#endif } #region IDriveConnectionCommandProvider Members @@ -36,5 +47,24 @@ public DbCommand CreateCommand() } #endregion + +#if NET6_0_OR_GREATER + + private Lazy _canCreateBatch; + + public DbBatch CreateBatch() + { + using (var connection = CreateConnection()) + { + var batch = connection.CreateBatch(); + batch.Connection = null; + return batch; + } + } + + public bool CanCreateBatch => _canCreateBatch.Value; + + +#endif } -} \ No newline at end of file +} diff --git a/src/NHibernate/ITransaction.cs b/src/NHibernate/ITransaction.cs index 9ed0a1dff78..e4ab02556d9 100644 --- a/src/NHibernate/ITransaction.cs +++ b/src/NHibernate/ITransaction.cs @@ -63,7 +63,7 @@ public partial interface ITransaction : IDisposable bool WasCommitted { get; } /// - /// Enlist the in the current Transaction. + /// Enlist a in the current Transaction. /// /// The to enlist. /// @@ -83,6 +83,20 @@ public partial interface ITransaction : IDisposable void RegisterSynchronization(ISynchronization synchronization); } +#if NET6_0_OR_GREATER + internal interface ITransactionWithBatchSupport : ITransaction + { + /// + /// Enlist a in the current Transaction. + /// + /// The to enlist. + /// + /// It is okay for this to be a no op implementation. + /// + void Enlist(DbBatch batch); + } +#endif + // 6.0 TODO: merge into ITransaction public static class TransactionExtensions { diff --git a/src/NHibernate/Transaction/AdoTransaction.cs b/src/NHibernate/Transaction/AdoTransaction.cs index 8adc842d936..0de4399a9e3 100644 --- a/src/NHibernate/Transaction/AdoTransaction.cs +++ b/src/NHibernate/Transaction/AdoTransaction.cs @@ -13,6 +13,9 @@ namespace NHibernate.Transaction /// the interface. /// public partial class AdoTransaction : ITransaction +#if NET6_0_OR_GREATER + , ITransactionWithBatchSupport +#endif { private static readonly INHibernateLogger log = NHibernateLogger.For(typeof(AdoTransaction)); private ISessionImplementor session; @@ -86,6 +89,58 @@ public void Enlist(DbCommand command) } } +#if NET6_0_OR_GREATER + /// + /// Enlist a in the current . + /// + /// The to enlist in this Transaction. + /// + /// + /// This takes care of making sure the 's Transaction property + /// contains the correct or if there is no + /// Transaction for the ISession - ie BeginTransaction() not called. + /// + /// + /// This method may be called even when the transaction is disposed. + /// + /// + public void Enlist(DbBatch batch) + { + if (trans == null) + { + if (log.IsWarnEnabled()) + { + if (batch.Transaction != null) + { + log.Warn("set a nonnull DbCommand.Transaction to null because the Session had no Transaction"); + } + } + + batch.Transaction = null; + return; + } + else + { + if (log.IsWarnEnabled()) + { + // got into here because the command was being initialized and had a null Transaction - probably + // don't need to be confused by that - just a normal part of initialization... + if (batch.Transaction != null && batch.Transaction != trans) + { + log.Warn("The DbCommand had a different Transaction than the Session. This can occur when " + + "Disconnecting and Reconnecting Sessions because the PreparedCommand Cache is Session specific."); + } + } + log.Debug("Enlist Command"); + + // If you try to assign a disposed transaction to a command with MSSQL, it will leave the command's + // transaction as null and not throw an error. With SQLite, for example, it will throw an exception + // here instead. Because of this, we set the trans field to null in when Dispose is called. + batch.Transaction = trans; + } + } +#endif + // Since 5.2 [Obsolete("Use RegisterSynchronization(ITransactionCompletionSynchronization) instead")] public void RegisterSynchronization(ISynchronization sync) From 3c08e66785883b7bfee728ccc8d856d815ec5a56 Mon Sep 17 00:00:00 2001 From: Gunnar Liljas Date: Fri, 15 Mar 2024 03:02:42 +0100 Subject: [PATCH 02/12] TrustServerCertificate=true --- .github/workflows/NetCoreTests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/NetCoreTests.yml b/.github/workflows/NetCoreTests.yml index 093c1e99534..da628006784 100644 --- a/.github/workflows/NetCoreTests.yml +++ b/.github/workflows/NetCoreTests.yml @@ -16,7 +16,7 @@ jobs: DB_INIT: | docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=P@ssw0rd" -e "MSSQL_PID=Express" -p 1433:1433 -d --name sqlexpress mcr.microsoft.com/mssql/server:2019-latest; - DB: SqlServer2008-MicrosoftDataSqlClientDriver - CONNECTION_STRING: "Server=localhost;initial catalog=nhibernate;User Id=sa;Password=P@ssw0rd;packet size=4096;" + CONNECTION_STRING: "Server=localhost;initial catalog=nhibernate;User Id=sa;Password=P@ssw0rd;packet size=4096;TrustServerCertificate=true;" OS: ubuntu-latest DB_INIT: | docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=P@ssw0rd" -e "MSSQL_PID=Express" -p 1433:1433 -d --name sqlexpress mcr.microsoft.com/mssql/server:2019-latest; From dec87ec1dab80c022ba4dd3d8950205039a8bb9c Mon Sep 17 00:00:00 2001 From: Gunnar Liljas Date: Fri, 15 Mar 2024 10:16:44 +0100 Subject: [PATCH 03/12] Use interface default implementations --- src/NHibernate.Test/Ado/BatcherFixture.cs | 4 +- src/NHibernate/AdoNet/ConnectionManager.cs | 6 +-- src/NHibernate/AdoNet/DbBatchBatcher.cs | 52 ++++++++++++++++--- ...erFactoryDriveConnectionCommandProvider.cs | 8 +-- src/NHibernate/Driver/DriverBase.cs | 3 -- .../Driver/IDriveConnectionCommandProvider.cs | 7 ++- ...nnectionCommandProviderWithBatchSupport.cs | 15 ------ src/NHibernate/Driver/IDriver.cs | 10 +++- .../Driver/IDriverWithBatchSupport.cs | 16 ------ .../Driver/ReflectionBasedDriver.cs | 21 +------- ...eflectionDriveConnectionCommandProvider.cs | 6 --- src/NHibernate/ITransaction.cs | 7 +-- src/NHibernate/Transaction/AdoTransaction.cs | 9 ++-- 13 files changed, 71 insertions(+), 93 deletions(-) delete mode 100644 src/NHibernate/Driver/IDriveConnectionCommandProviderWithBatchSupport.cs delete mode 100644 src/NHibernate/Driver/IDriverWithBatchSupport.cs diff --git a/src/NHibernate.Test/Ado/BatcherFixture.cs b/src/NHibernate.Test/Ado/BatcherFixture.cs index b54a0a73e67..ff3bfd7aace 100644 --- a/src/NHibernate.Test/Ado/BatcherFixture.cs +++ b/src/NHibernate.Test/Ado/BatcherFixture.cs @@ -168,7 +168,7 @@ public void BatchedoutputShouldBeFormatted() { FillDb(); var log = sqlLog.GetWholeLog(); - Assert.IsTrue(log.Contains("INSERT \n INTO")); + Assert.That(log, Does.Contain("INSERT \n INTO").IgnoreCase); } Cleanup(); @@ -239,7 +239,7 @@ public void AbstractBatcherLog() foreach (var loggingEvent in sl.Appender.GetEvents()) { string message = loggingEvent.RenderedMessage; - if(message.ToLowerInvariant().Contains("insert")) + if(message.Contains("insert")) { Assert.That(message, Does.Contain("batch").IgnoreCase); } diff --git a/src/NHibernate/AdoNet/ConnectionManager.cs b/src/NHibernate/AdoNet/ConnectionManager.cs index e258e5b9232..8cd5d4c0533 100644 --- a/src/NHibernate/AdoNet/ConnectionManager.cs +++ b/src/NHibernate/AdoNet/ConnectionManager.cs @@ -524,15 +524,15 @@ public void EnlistInTransaction(DbBatch batch) if (batch == null) throw new ArgumentNullException(nameof(batch)); - if (_transaction is ITransactionWithBatchSupport transactionWithBatch) + if (_transaction != null) { - transactionWithBatch.Enlist(batch); + _transaction.Enlist(batch); return; } if (batch.Transaction != null) { - _log.Warn("set a nonnull DbCommand.Transaction to null because the Session had no Transaction"); + _log.Warn("set a nonnull DbBatch.Transaction to null because the Session had no Transaction"); batch.Transaction = null; } } diff --git a/src/NHibernate/AdoNet/DbBatchBatcher.cs b/src/NHibernate/AdoNet/DbBatchBatcher.cs index 1bfb9703262..2dfc56a2534 100644 --- a/src/NHibernate/AdoNet/DbBatchBatcher.cs +++ b/src/NHibernate/AdoNet/DbBatchBatcher.cs @@ -1,4 +1,4 @@ -#if NET6_0_OR_GREATER +#if NET6_0_OR_GREATER using System; using System.Data.Common; using System.Linq; @@ -26,7 +26,7 @@ public DbBatchBatcher(ConnectionManager connectionManager, IInterceptor intercep { _batchSize = Factory.Settings.AdoBatchSize; _defaultTimeout = Driver.GetCommandTimeout(); - + _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 @@ -152,7 +152,7 @@ protected override void DoExecuteBatch(DbCommand ps) { throw ADOExceptionHelper.Convert(Factory.SQLExceptionConverter, e, "could not execute batch command."); } - + Expectations.VerifyOutcomeBatched(_totalExpectedRowsAffected, rowsAffected, ps); } finally @@ -168,8 +168,7 @@ protected override async Task DoExecuteBatchAsync(DbCommand ps, CancellationToke { Log.Debug("Executing batch"); await (CheckReadersAsync(cancellationToken)).ConfigureAwait(false); - //await (PrepareAsync(_currentBatch, cancellationToken)).ConfigureAwait(false); - Prepare(_currentBatch); + await (PrepareAsync(_currentBatch, cancellationToken)).ConfigureAwait(false); if (Factory.Settings.SqlStatementLogger.IsDebugEnabled) { Factory.Settings.SqlStatementLogger.LogBatchCommand(_currentBatchCommandsLog.ToString()); @@ -194,7 +193,7 @@ protected override async Task DoExecuteBatchAsync(DbCommand ps, CancellationToke private DbBatch CreateConfiguredBatch() { - var result = ((IDriverWithBatchSupport) Driver).CreateBatch(); + var result = Driver.CreateBatch(); if (_defaultTimeout > 0) { try @@ -218,7 +217,7 @@ private void ClearCurrentBatch() _currentBatch.Dispose(); _totalExpectedRowsAffected = 0; _currentBatch = CreateConfiguredBatch(); - + if (Factory.Settings.SqlStatementLogger.IsDebugEnabled) { _currentBatchCommandsLog = new StringBuilder().AppendLine("Batch commands:"); @@ -285,7 +284,44 @@ protected void Prepare(DbBatch batch) } _connectionManager.EnlistInTransaction(batch); - (Driver as IDriverWithBatchSupport).PrepareBatch(batch); + Driver.PrepareBatch(batch); + } + catch (InvalidOperationException ioe) + { + throw new ADOException("While preparing " + string.Join(Environment.NewLine, batch.BatchCommands.Select(x => x.CommandText)) + " an error occurred", ioe); + } + } + + /// + /// Prepares the for execution in the database. + /// + /// + /// This takes care of hooking the up to an + /// and if one exists. It will call Prepare if the Driver + /// supports preparing batches. + /// + protected async Task PrepareAsync(DbBatch batch, CancellationToken cancellationToken) + { + try + { + var sessionConnection = await _connectionManager.GetConnectionAsync(cancellationToken).ConfigureAwait(false); + + if (batch.Connection != null) + { + // make sure the commands connection is the same as the Sessions connection + // these can be different when the session is disconnected and then reconnected + if (batch.Connection != sessionConnection) + { + batch.Connection = sessionConnection; + } + } + else + { + batch.Connection = sessionConnection; + } + + _connectionManager.EnlistInTransaction(batch); + Driver.PrepareBatch(batch); } catch (InvalidOperationException ioe) { diff --git a/src/NHibernate/Driver/DbProviderFactoryDriveConnectionCommandProvider.cs b/src/NHibernate/Driver/DbProviderFactoryDriveConnectionCommandProvider.cs index 6467983357a..337ce6bf650 100644 --- a/src/NHibernate/Driver/DbProviderFactoryDriveConnectionCommandProvider.cs +++ b/src/NHibernate/Driver/DbProviderFactoryDriveConnectionCommandProvider.cs @@ -4,9 +4,6 @@ namespace NHibernate.Driver { public class DbProviderFactoryDriveConnectionCommandProvider : IDriveConnectionCommandProvider -#if NET6_0_OR_GREATER - , IDriveConnectionCommandProviderWithBatchSupport -#endif { private readonly DbProviderFactory dbProviderFactory; @@ -29,10 +26,7 @@ public DbCommand CreateCommand() return dbProviderFactory.CreateCommand(); } #if NET6_0_OR_GREATER - public DbBatch CreateBatch() - { - return dbProviderFactory.CreateBatch(); - } + public DbBatch CreateBatch() => dbProviderFactory.CreateBatch(); public bool CanCreateBatch => dbProviderFactory.CanCreateBatch && dbProviderFactory.CreateCommand() is ICloneable; #endif diff --git a/src/NHibernate/Driver/DriverBase.cs b/src/NHibernate/Driver/DriverBase.cs index 4b692af6347..eaee002c607 100644 --- a/src/NHibernate/Driver/DriverBase.cs +++ b/src/NHibernate/Driver/DriverBase.cs @@ -15,9 +15,6 @@ namespace NHibernate.Driver /// Base class for the implementation of IDriver /// public abstract class DriverBase : IDriver, ISqlParameterFormatter -#if NET6_0_OR_GREATER - , IDriverWithBatchSupport -#endif { private static readonly INHibernateLogger log = NHibernateLogger.For(typeof(DriverBase)); diff --git a/src/NHibernate/Driver/IDriveConnectionCommandProvider.cs b/src/NHibernate/Driver/IDriveConnectionCommandProvider.cs index db6de57696d..12df92b7ddb 100644 --- a/src/NHibernate/Driver/IDriveConnectionCommandProvider.cs +++ b/src/NHibernate/Driver/IDriveConnectionCommandProvider.cs @@ -1,3 +1,4 @@ +using System; using System.Data.Common; namespace NHibernate.Driver @@ -6,5 +7,9 @@ public interface IDriveConnectionCommandProvider { DbConnection CreateConnection(); DbCommand CreateCommand(); +#if NET6_0_OR_GREATER + DbBatch CreateBatch() => throw new NotImplementedException(); + bool CanCreateBatch => false; +#endif } -} \ No newline at end of file +} diff --git a/src/NHibernate/Driver/IDriveConnectionCommandProviderWithBatchSupport.cs b/src/NHibernate/Driver/IDriveConnectionCommandProviderWithBatchSupport.cs deleted file mode 100644 index dd9235de56b..00000000000 --- a/src/NHibernate/Driver/IDriveConnectionCommandProviderWithBatchSupport.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Data.Common; - -namespace NHibernate.Driver -{ -#if NET6_0_OR_GREATER - //TODO: Include in IDriveConnectionCommandProvider for NET6_0_OR_GREATER - internal interface IDriveConnectionCommandProviderWithBatchSupport : IDriveConnectionCommandProvider - { - DbBatch CreateBatch(); - bool CanCreateBatch { get; } - - } -#endif - -} diff --git a/src/NHibernate/Driver/IDriver.cs b/src/NHibernate/Driver/IDriver.cs index 2e723286635..479166362f0 100644 --- a/src/NHibernate/Driver/IDriver.cs +++ b/src/NHibernate/Driver/IDriver.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.Data; using System.Data.Common; -using System.Linq; -using System.Windows.Input; using NHibernate.Engine; using NHibernate.SqlCommand; using NHibernate.SqlTypes; @@ -165,5 +163,13 @@ public interface IDriver /// The minimal date supplied as a supported by this driver. /// DateTime MinDate { get; } + +#if NET6_0_OR_GREATER + DbBatch CreateBatch() => throw new NotImplementedException(); + bool CanCreateBatch => false; + + void AdjustBatch(DbBatch dbBatch) => throw new NotImplementedException(); + void PrepareBatch(DbBatch dbBatch) => throw new NotImplementedException(); +#endif } } diff --git a/src/NHibernate/Driver/IDriverWithBatchSupport.cs b/src/NHibernate/Driver/IDriverWithBatchSupport.cs deleted file mode 100644 index 60e5236906f..00000000000 --- a/src/NHibernate/Driver/IDriverWithBatchSupport.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Data.Common; - -namespace NHibernate.Driver -{ -#if NET6_0_OR_GREATER - //TODO: Include in IDriver for NET6_0_OR_GREATER - internal interface IDriverWithBatchSupport : IDriver - { - public DbBatch CreateBatch(); - public bool CanCreateBatch{ get; } - - public void AdjustBatch(DbBatch dbBatch); - public void PrepareBatch(DbBatch dbBatch); - } -#endif -} diff --git a/src/NHibernate/Driver/ReflectionBasedDriver.cs b/src/NHibernate/Driver/ReflectionBasedDriver.cs index 0ecd482e73e..f9664726e3f 100644 --- a/src/NHibernate/Driver/ReflectionBasedDriver.cs +++ b/src/NHibernate/Driver/ReflectionBasedDriver.cs @@ -75,26 +75,9 @@ public override DbCommand CreateCommand() } #if NET6_0_OR_GREATER - public override DbBatch CreateBatch() - { - if (connectionCommandProvider is IDriveConnectionCommandProviderWithBatchSupport driveConnectionCommandProviderWithBatch) - { - return driveConnectionCommandProviderWithBatch.CreateBatch(); - } - throw new NotSupportedException(); - } + public override DbBatch CreateBatch() => connectionCommandProvider.CreateBatch(); - public override bool CanCreateBatch - { - get - { - if (connectionCommandProvider is IDriveConnectionCommandProviderWithBatchSupport driveConnectionCommandProviderWithBatch) - { - return driveConnectionCommandProviderWithBatch.CanCreateBatch; - } - return false; - } - } + public override bool CanCreateBatch => connectionCommandProvider.CanCreateBatch; #endif } } diff --git a/src/NHibernate/Driver/ReflectionDriveConnectionCommandProvider.cs b/src/NHibernate/Driver/ReflectionDriveConnectionCommandProvider.cs index 071443498a7..b4705f6a4ed 100644 --- a/src/NHibernate/Driver/ReflectionDriveConnectionCommandProvider.cs +++ b/src/NHibernate/Driver/ReflectionDriveConnectionCommandProvider.cs @@ -5,9 +5,6 @@ namespace NHibernate.Driver { public class ReflectionDriveConnectionCommandProvider : IDriveConnectionCommandProvider -#if NET6_0_OR_GREATER - , IDriveConnectionCommandProviderWithBatchSupport -#endif { private readonly System.Type commandType; private readonly System.Type connectionType; @@ -49,7 +46,6 @@ public DbCommand CreateCommand() #endregion #if NET6_0_OR_GREATER - private Lazy _canCreateBatch; public DbBatch CreateBatch() @@ -63,8 +59,6 @@ public DbBatch CreateBatch() } public bool CanCreateBatch => _canCreateBatch.Value; - - #endif } } diff --git a/src/NHibernate/ITransaction.cs b/src/NHibernate/ITransaction.cs index e4ab02556d9..e9b0d6bec48 100644 --- a/src/NHibernate/ITransaction.cs +++ b/src/NHibernate/ITransaction.cs @@ -81,11 +81,8 @@ public partial interface ITransaction : IDisposable "RegisterSynchronization(ITransactionCompletionSynchronization)': the TransactionExtensions extension " + "method will call it.")] void RegisterSynchronization(ISynchronization synchronization); - } #if NET6_0_OR_GREATER - internal interface ITransactionWithBatchSupport : ITransaction - { /// /// Enlist a in the current Transaction. /// @@ -93,9 +90,9 @@ internal interface ITransactionWithBatchSupport : ITransaction /// /// It is okay for this to be a no op implementation. /// - void Enlist(DbBatch batch); - } + void Enlist(DbBatch batch) => throw new NotImplementedException(); #endif + } // 6.0 TODO: merge into ITransaction public static class TransactionExtensions diff --git a/src/NHibernate/Transaction/AdoTransaction.cs b/src/NHibernate/Transaction/AdoTransaction.cs index 0de4399a9e3..5ad87538bbb 100644 --- a/src/NHibernate/Transaction/AdoTransaction.cs +++ b/src/NHibernate/Transaction/AdoTransaction.cs @@ -13,9 +13,6 @@ namespace NHibernate.Transaction /// the interface. /// public partial class AdoTransaction : ITransaction -#if NET6_0_OR_GREATER - , ITransactionWithBatchSupport -#endif { private static readonly INHibernateLogger log = NHibernateLogger.For(typeof(AdoTransaction)); private ISessionImplementor session; @@ -112,7 +109,7 @@ public void Enlist(DbBatch batch) { if (batch.Transaction != null) { - log.Warn("set a nonnull DbCommand.Transaction to null because the Session had no Transaction"); + log.Warn("set a nonnull DbBatch.Transaction to null because the Session had no Transaction"); } } @@ -127,11 +124,11 @@ public void Enlist(DbBatch batch) // don't need to be confused by that - just a normal part of initialization... if (batch.Transaction != null && batch.Transaction != trans) { - log.Warn("The DbCommand had a different Transaction than the Session. This can occur when " + + log.Warn("The DbBatch had a different Transaction than the Session. This can occur when " + "Disconnecting and Reconnecting Sessions because the PreparedCommand Cache is Session specific."); } } - log.Debug("Enlist Command"); + log.Debug("Enlist DbBatch"); // If you try to assign a disposed transaction to a command with MSSQL, it will leave the command's // transaction as null and not throw an error. With SQLite, for example, it will throw an exception From 7834f53990cbd0ba5f21fbd5acfd057aac559e90 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 15 Mar 2024 09:18:53 +0000 Subject: [PATCH 04/12] Generate async files --- src/NHibernate.Test/Async/Ado/BatcherFixture.cs | 4 ++-- src/NHibernate/Async/ITransaction.cs | 3 +++ src/NHibernate/Async/Transaction/AdoTransaction.cs | 2 -- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/NHibernate.Test/Async/Ado/BatcherFixture.cs b/src/NHibernate.Test/Async/Ado/BatcherFixture.cs index 6ccd4f30a72..96146f114a3 100644 --- a/src/NHibernate.Test/Async/Ado/BatcherFixture.cs +++ b/src/NHibernate.Test/Async/Ado/BatcherFixture.cs @@ -140,7 +140,7 @@ public async Task BatchedoutputShouldBeFormattedAsync() { await (FillDbAsync()); var log = sqlLog.GetWholeLog(); - Assert.IsTrue(log.Contains("INSERT \n INTO")); + Assert.That(log, Does.Contain("INSERT \n INTO").IgnoreCase); } await (CleanupAsync()); @@ -211,7 +211,7 @@ public async Task AbstractBatcherLogAsync() foreach (var loggingEvent in sl.Appender.GetEvents()) { string message = loggingEvent.RenderedMessage; - if(message.ToLowerInvariant().Contains("insert")) + if(message.Contains("insert")) { Assert.That(message, Does.Contain("batch").IgnoreCase); } diff --git a/src/NHibernate/Async/ITransaction.cs b/src/NHibernate/Async/ITransaction.cs index ea6ea4b3ab9..c76af0124de 100644 --- a/src/NHibernate/Async/ITransaction.cs +++ b/src/NHibernate/Async/ITransaction.cs @@ -35,5 +35,8 @@ public partial interface ITransaction : IDisposable /// /// A cancellation token that can be used to cancel the work Task RollbackAsync(CancellationToken cancellationToken = default(CancellationToken)); + +#if NET6_0_OR_GREATER +#endif } } diff --git a/src/NHibernate/Async/Transaction/AdoTransaction.cs b/src/NHibernate/Async/Transaction/AdoTransaction.cs index e1f7497fab6..a8b249130fa 100644 --- a/src/NHibernate/Async/Transaction/AdoTransaction.cs +++ b/src/NHibernate/Async/Transaction/AdoTransaction.cs @@ -21,8 +21,6 @@ namespace NHibernate.Transaction using System.Threading.Tasks; using System.Threading; public partial class AdoTransaction : ITransaction -#if NET6_0_OR_GREATER -#endif { private async Task AfterTransactionCompletionAsync(bool successful, CancellationToken cancellationToken) From d62892ac060e74360c557e9522ae44720871d81d Mon Sep 17 00:00:00 2001 From: Alex Zaytsev Date: Fri, 15 Mar 2024 21:36:44 +1000 Subject: [PATCH 05/12] Fix missing possible async calls. Enforce rules. --- .editorconfig | 3 +++ src/NHibernate.sln | 1 + src/NHibernate/Action/BulkOperationCleanupAction.cs | 4 ++-- src/NHibernate/AdoNet/DbBatchBatcher.cs | 6 +++--- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.editorconfig b/.editorconfig index 15901d7cb25..983372b5b1f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -27,6 +27,9 @@ dotnet_diagnostic.NUnit2031.severity = suggestion dotnet_diagnostic.NUnit2049.severity = suggestion # The SameAs constraint always fails on value types as the actual and the expected value cannot be the same reference dotnet_diagnostic.NUnit2040.severity = suggestion +dotnet_diagnostic.CA1849.severity = error +dotnet_diagnostic.CA2007.severity = error +dotnet_code_quality.CA2007.output_kind = DynamicallyLinkedLibrary [*.xsd] indent_style = tab diff --git a/src/NHibernate.sln b/src/NHibernate.sln index 4eec9b87633..0b2a7f25d0b 100644 --- a/src/NHibernate.sln +++ b/src/NHibernate.sln @@ -13,6 +13,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ..\appveyor.yml = ..\appveyor.yml ..\ReleaseProcedure.txt = ..\ReleaseProcedure.txt ..\global.json = ..\global.json + ..\.editorconfig = ..\.editorconfig EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NHibernate", "NHibernate\NHibernate.csproj", "{5909BFE7-93CF-4E5F-BE22-6293368AF01D}" diff --git a/src/NHibernate/Action/BulkOperationCleanupAction.cs b/src/NHibernate/Action/BulkOperationCleanupAction.cs index 5308d2834d7..2558e497610 100644 --- a/src/NHibernate/Action/BulkOperationCleanupAction.cs +++ b/src/NHibernate/Action/BulkOperationCleanupAction.cs @@ -166,8 +166,8 @@ public virtual void Init() [Obsolete("This method has no more usage in NHibernate and will be removed in a future version.")] public virtual async Task InitAsync(CancellationToken cancellationToken) { - await EvictEntityRegionsAsync(cancellationToken); - await EvictCollectionRegionsAsync(cancellationToken); + await EvictEntityRegionsAsync(cancellationToken).ConfigureAwait(false); + await EvictCollectionRegionsAsync(cancellationToken).ConfigureAwait(false); } } } diff --git a/src/NHibernate/AdoNet/DbBatchBatcher.cs b/src/NHibernate/AdoNet/DbBatchBatcher.cs index 2dfc56a2534..f508d76efd8 100644 --- a/src/NHibernate/AdoNet/DbBatchBatcher.cs +++ b/src/NHibernate/AdoNet/DbBatchBatcher.cs @@ -167,8 +167,8 @@ protected override async Task DoExecuteBatchAsync(DbCommand ps, CancellationToke try { Log.Debug("Executing batch"); - await (CheckReadersAsync(cancellationToken)).ConfigureAwait(false); - await (PrepareAsync(_currentBatch, cancellationToken)).ConfigureAwait(false); + await CheckReadersAsync(cancellationToken).ConfigureAwait(false); + await PrepareAsync(_currentBatch, cancellationToken).ConfigureAwait(false); if (Factory.Settings.SqlStatementLogger.IsDebugEnabled) { Factory.Settings.SqlStatementLogger.LogBatchCommand(_currentBatchCommandsLog.ToString()); @@ -176,7 +176,7 @@ protected override async Task DoExecuteBatchAsync(DbCommand ps, CancellationToke int rowsAffected; try { - rowsAffected = _currentBatch.ExecuteNonQuery(); + rowsAffected = await _currentBatch.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false); } catch (DbException e) { From c31b36c54a2411db4b34162449ee2cc9c4a042f3 Mon Sep 17 00:00:00 2001 From: Alex Zaytsev Date: Tue, 23 Apr 2024 13:10:54 +1000 Subject: [PATCH 06/12] Reduce code duplication --- src/NHibernate/AdoNet/DbBatchBatcher.cs | 68 ++++++++++--------------- 1 file changed, 28 insertions(+), 40 deletions(-) diff --git a/src/NHibernate/AdoNet/DbBatchBatcher.cs b/src/NHibernate/AdoNet/DbBatchBatcher.cs index f508d76efd8..35e795d50cf 100644 --- a/src/NHibernate/AdoNet/DbBatchBatcher.cs +++ b/src/NHibernate/AdoNet/DbBatchBatcher.cs @@ -7,7 +7,6 @@ using System.Threading.Tasks; using NHibernate.AdoNet.Util; using NHibernate.Driver; -using NHibernate.Engine; using NHibernate.Exceptions; namespace NHibernate.AdoNet @@ -38,20 +37,14 @@ public DbBatchBatcher(ConnectionManager connectionManager, IInterceptor intercep public override int BatchSize { - get { return _batchSize; } - set { _batchSize = value; } + get => _batchSize; + set => _batchSize = value; } - protected override int CountOfStatementsInCurrentBatch - { - get { return _currentBatch.BatchCommands.Count; } - } + protected override int CountOfStatementsInCurrentBatch => _currentBatch.BatchCommands.Count; - public override void AddToBatch(IExpectation expectation) + private void LogBatchCommand(DbCommand batchUpdate) { - _totalExpectedRowsAffected += expectation.ExpectedRowCount; - var batchUpdate = CurrentCommand; - Driver.AdjustCommand(batchUpdate); string lineWithParameters = null; var sqlStatementLogger = Factory.Settings.SqlStatementLogger; if (sqlStatementLogger.IsDebugEnabled || Log.IsDebugEnabled()) @@ -60,21 +53,15 @@ public override void AddToBatch(IExpectation expectation) var formatStyle = sqlStatementLogger.DetermineActualStyle(FormatStyle.Basic); lineWithParameters = formatStyle.Formatter.Format(lineWithParameters); _currentBatchCommandsLog.Append("command ") - .Append(_currentBatch.BatchCommands.Count) - .Append(":") - .AppendLine(lineWithParameters); + .Append(_currentBatch.BatchCommands.Count) + .Append(':') + .AppendLine(lineWithParameters); } + if (Log.IsDebugEnabled()) { Log.Debug("Adding to batch:{0}", lineWithParameters); } - - AddCommandToBatch(batchUpdate); - - if (CountOfStatementsInCurrentBatch >= _batchSize) - { - ExecuteBatchWithTiming(batchUpdate); - } } private void AddCommandToBatch(DbCommand batchUpdate) @@ -87,48 +74,49 @@ private void AddCommandToBatch(DbCommand batchUpdate) { dbBatchCommand.Parameters.Add(((ICloneable) param).Clone()); } + _currentBatch.BatchCommands.Add(dbBatchCommand); } + public override void AddToBatch(IExpectation expectation) + { + _totalExpectedRowsAffected += expectation.ExpectedRowCount; + var batchUpdate = CurrentCommand; + Driver.AdjustCommand(batchUpdate); + LogBatchCommand(batchUpdate); + AddCommandToBatch(batchUpdate); + + if (CountOfStatementsInCurrentBatch >= _batchSize) + { + ExecuteBatchWithTiming(batchUpdate); + } + } + public override Task AddToBatchAsync(IExpectation expectation, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { - return Task.FromCanceled(cancellationToken); + return Task.FromCanceled(cancellationToken); } + try { _totalExpectedRowsAffected += expectation.ExpectedRowCount; var 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.BatchCommands.Count) - .Append(":") - .AppendLine(lineWithParameters); - } - if (Log.IsDebugEnabled()) - { - Log.Debug("Adding to batch:{0}", lineWithParameters); - } - + LogBatchCommand(batchUpdate); AddCommandToBatch(batchUpdate); if (CountOfStatementsInCurrentBatch >= _batchSize) { return ExecuteBatchWithTimingAsync(batchUpdate, cancellationToken); } + return Task.CompletedTask; } catch (Exception ex) { - return Task.FromException(ex); + return Task.FromException(ex); } } From 32e30d285cd1392e71b1aadfc3903c00e9abe686 Mon Sep 17 00:00:00 2001 From: Gunnar Liljas Date: Tue, 23 Apr 2024 16:16:58 +0200 Subject: [PATCH 07/12] Moved the creation of DbBatchCommands to the driver --- src/NHibernate/AdoNet/DbBatchBatcher.cs | 11 ++---- ...erFactoryDriveConnectionCommandProvider.cs | 2 +- src/NHibernate/Driver/DriverBase.cs | 19 ++++++++++ src/NHibernate/Driver/IDriver.cs | 35 +++++++++++++++++++ ...eflectionDriveConnectionCommandProvider.cs | 2 +- 5 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/NHibernate/AdoNet/DbBatchBatcher.cs b/src/NHibernate/AdoNet/DbBatchBatcher.cs index 35e795d50cf..4914a107764 100644 --- a/src/NHibernate/AdoNet/DbBatchBatcher.cs +++ b/src/NHibernate/AdoNet/DbBatchBatcher.cs @@ -66,15 +66,8 @@ private void LogBatchCommand(DbCommand batchUpdate) private void AddCommandToBatch(DbCommand batchUpdate) { - var dbBatchCommand = _currentBatch.CreateBatchCommand(); - dbBatchCommand.CommandText = batchUpdate.CommandText; - dbBatchCommand.CommandType = batchUpdate.CommandType; - - foreach (var param in batchUpdate.Parameters) - { - dbBatchCommand.Parameters.Add(((ICloneable) param).Clone()); - } - + var dbBatchCommand = Driver.CreateDbBatchCommandFromDbCommand(_currentBatch, batchUpdate); + _currentBatch.BatchCommands.Add(dbBatchCommand); } diff --git a/src/NHibernate/Driver/DbProviderFactoryDriveConnectionCommandProvider.cs b/src/NHibernate/Driver/DbProviderFactoryDriveConnectionCommandProvider.cs index 337ce6bf650..d1773548451 100644 --- a/src/NHibernate/Driver/DbProviderFactoryDriveConnectionCommandProvider.cs +++ b/src/NHibernate/Driver/DbProviderFactoryDriveConnectionCommandProvider.cs @@ -28,7 +28,7 @@ public DbCommand CreateCommand() #if NET6_0_OR_GREATER public DbBatch CreateBatch() => dbProviderFactory.CreateBatch(); - public bool CanCreateBatch => dbProviderFactory.CanCreateBatch && dbProviderFactory.CreateCommand() is ICloneable; + public bool CanCreateBatch => dbProviderFactory.CanCreateBatch && dbProviderFactory.CreateCommand().CreateParameter() is ICloneable; #endif } } diff --git a/src/NHibernate/Driver/DriverBase.cs b/src/NHibernate/Driver/DriverBase.cs index eaee002c607..74feae035b0 100644 --- a/src/NHibernate/Driver/DriverBase.cs +++ b/src/NHibernate/Driver/DriverBase.cs @@ -371,6 +371,25 @@ public virtual DbBatch CreateBatch() public virtual bool CanCreateBatch => false; + /// + /// Override to use a custom mechanism to create a from a . + /// The default implementation relies on the parameters implementing (and properly supporting) + /// + /// + /// + /// + public virtual DbBatchCommand CreateDbBatchCommandFromDbCommand(DbBatch dbBatch, DbCommand dbCommand) + { + var dbBatchCommand = dbBatch.CreateBatchCommand(); + dbBatchCommand.CommandText = dbCommand.CommandText; + dbBatchCommand.CommandType = dbCommand.CommandType; + + foreach (var param in dbCommand.Parameters) + { + dbBatchCommand.Parameters.Add(((ICloneable) param).Clone()); + } + return dbBatchCommand; + } #endif diff --git a/src/NHibernate/Driver/IDriver.cs b/src/NHibernate/Driver/IDriver.cs index 479166362f0..ec77500dabf 100644 --- a/src/NHibernate/Driver/IDriver.cs +++ b/src/NHibernate/Driver/IDriver.cs @@ -165,11 +165,46 @@ public interface IDriver DateTime MinDate { get; } #if NET6_0_OR_GREATER + /// + /// Create a + /// + /// + /// DbBatch CreateBatch() => throw new NotImplementedException(); + + /// + /// Can this driver create es? + /// bool CanCreateBatch => false; + /// + /// Make any adjustments to each object before it is added to the batcher. + /// + /// The batch. + /// + /// This method should be executed before adding each single batch to the batcher. + /// If you have to adjust parameters values/type (when the command is fullfilled) this is a good place to do it. + /// void AdjustBatch(DbBatch dbBatch) => throw new NotImplementedException(); + + /// + /// Prepare the by calling . + /// May be a no-op if the driver does not support preparing commands, or for any other reason. + /// + /// The batch. void PrepareBatch(DbBatch dbBatch) => throw new NotImplementedException(); + + /// + /// Creates (clones) a from a , + /// copying its , + /// and all its parameters. + /// The returned will not be added to + /// + /// + /// + /// + /// + DbBatchCommand CreateDbBatchCommandFromDbCommand(DbBatch dbBatch, DbCommand dbCommand) => throw new NotImplementedException(); #endif } } diff --git a/src/NHibernate/Driver/ReflectionDriveConnectionCommandProvider.cs b/src/NHibernate/Driver/ReflectionDriveConnectionCommandProvider.cs index b4705f6a4ed..7ff2705c0c2 100644 --- a/src/NHibernate/Driver/ReflectionDriveConnectionCommandProvider.cs +++ b/src/NHibernate/Driver/ReflectionDriveConnectionCommandProvider.cs @@ -25,7 +25,7 @@ public ReflectionDriveConnectionCommandProvider(System.Type connectionType, Syst _canCreateBatch = new Lazy(() => { using (var connection = CreateConnection()) { - return connection.CanCreateBatch && connection.CreateCommand() is ICloneable; + return connection.CanCreateBatch && connection.CreateCommand().CreateParameter() is ICloneable; } }); #endif From ff3ca4f9c0915abd3f33c06029fa77a1de6aedef Mon Sep 17 00:00:00 2001 From: Gunnar Liljas Date: Sat, 21 Dec 2024 22:31:31 +0100 Subject: [PATCH 08/12] TrustServerCertificate=true --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index e497e3469dc..9a26700c4cb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,7 +5,7 @@ environment: - DB: SqlServer2008 CONNECTION_STRING: Server=(local)\SQL2017;User ID=sa;Password=Password12!;initial catalog=nhibernate; - DB: SqlServer2008-MicrosoftDataSqlClientDriver - CONNECTION_STRING: Server=(local)\SQL2017;User ID=sa;Password=Password12!;initial catalog=nhibernate; + CONNECTION_STRING: Server=(local)\SQL2017;User ID=sa;Password=Password12!;initial catalog=nhibernate;TrustServerCertificate=true; - DB: Firebird - DB: Firebird4 - DB: MySQL From 6bc976138842d345bbb3083a1206228ff139a5ca Mon Sep 17 00:00:00 2001 From: Gunnar Liljas Date: Mon, 6 Jan 2025 20:15:08 +0100 Subject: [PATCH 09/12] Update src/NHibernate/Driver/ReflectionBasedDriver.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Frédéric Delaporte <12201973+fredericDelaporte@users.noreply.github.com> --- src/NHibernate/Driver/ReflectionBasedDriver.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/NHibernate/Driver/ReflectionBasedDriver.cs b/src/NHibernate/Driver/ReflectionBasedDriver.cs index f9664726e3f..365b7365a42 100644 --- a/src/NHibernate/Driver/ReflectionBasedDriver.cs +++ b/src/NHibernate/Driver/ReflectionBasedDriver.cs @@ -6,7 +6,8 @@ namespace NHibernate.Driver { public abstract class ReflectionBasedDriver : DriverBase { - protected const string ReflectionTypedProviderExceptionMessageTemplate = "The DbCommand and DbConnection implementation in the assembly {0} could not be found. " + protected const string ReflectionTypedProviderExceptionMessageTemplate = + "The DbCommand and DbConnection implementation in the assembly {0} could not be found. " + "Ensure that the assembly {0} is located in the application directory or in the Global " + "Assembly Cache. If the assembly is in the GAC, use element in the " + "application configuration file to specify the full name of the assembly."; From cdf3a4f173abdad4e61f787e1d6131e2e8113496 Mon Sep 17 00:00:00 2001 From: Gunnar Liljas Date: Mon, 6 Jan 2025 20:22:32 +0100 Subject: [PATCH 10/12] Removed AdjustBatch --- src/NHibernate/Driver/DriverBase.cs | 15 --------------- src/NHibernate/Driver/IDriver.cs | 10 ---------- src/NHibernate/Driver/ReflectionBasedDriver.cs | 6 +++--- 3 files changed, 3 insertions(+), 28 deletions(-) diff --git a/src/NHibernate/Driver/DriverBase.cs b/src/NHibernate/Driver/DriverBase.cs index 74feae035b0..abf48081196 100644 --- a/src/NHibernate/Driver/DriverBase.cs +++ b/src/NHibernate/Driver/DriverBase.cs @@ -332,7 +332,6 @@ public virtual void AdjustCommand(DbCommand command) #if NET6_0_OR_GREATER public void PrepareBatch(DbBatch batch) { - AdjustBatch(batch); OnBeforePrepare(batch); if (SupportsPreparingCommands && prepareSql) @@ -350,20 +349,6 @@ protected virtual void OnBeforePrepare(DbBatch command) { } - /// - /// Override to make any adjustments to each DbBatch object before it added to the batcher. - /// - /// The batch. - /// - /// This method is similar to the but, instead be called just before execute the command (that can be a batch) - /// is executed before add each single command to the batcher and before . - /// If you have to adjust parameters values/type (when the command is full filled) this is a good place where do it. - /// - public virtual void AdjustBatch(DbBatch batch) - { - - } - public virtual DbBatch CreateBatch() { throw new NotImplementedException(); diff --git a/src/NHibernate/Driver/IDriver.cs b/src/NHibernate/Driver/IDriver.cs index ec77500dabf..06ebd52d0d6 100644 --- a/src/NHibernate/Driver/IDriver.cs +++ b/src/NHibernate/Driver/IDriver.cs @@ -177,16 +177,6 @@ public interface IDriver /// bool CanCreateBatch => false; - /// - /// Make any adjustments to each object before it is added to the batcher. - /// - /// The batch. - /// - /// This method should be executed before adding each single batch to the batcher. - /// If you have to adjust parameters values/type (when the command is fullfilled) this is a good place to do it. - /// - void AdjustBatch(DbBatch dbBatch) => throw new NotImplementedException(); - /// /// Prepare the by calling . /// May be a no-op if the driver does not support preparing commands, or for any other reason. diff --git a/src/NHibernate/Driver/ReflectionBasedDriver.cs b/src/NHibernate/Driver/ReflectionBasedDriver.cs index 365b7365a42..6063848d2f0 100644 --- a/src/NHibernate/Driver/ReflectionBasedDriver.cs +++ b/src/NHibernate/Driver/ReflectionBasedDriver.cs @@ -8,9 +8,9 @@ public abstract class ReflectionBasedDriver : DriverBase { protected const string ReflectionTypedProviderExceptionMessageTemplate = "The DbCommand and DbConnection implementation in the assembly {0} could not be found. " - + "Ensure that the assembly {0} is located in the application directory or in the Global " - + "Assembly Cache. If the assembly is in the GAC, use element in the " - + "application configuration file to specify the full name of the assembly."; + + "Ensure that the assembly {0} is located in the application directory or in the Global " + + "Assembly Cache. If the assembly is in the GAC, use element in the " + + "application configuration file to specify the full name of the assembly."; private readonly IDriveConnectionCommandProvider connectionCommandProvider; From 64805c7b99af9d3d51478660789b541207b64019 Mon Sep 17 00:00:00 2001 From: Gunnar Liljas Date: Tue, 7 Jan 2025 23:27:05 +0100 Subject: [PATCH 11/12] Update src/NHibernate/Driver/DriverBase.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Frédéric Delaporte <12201973+fredericDelaporte@users.noreply.github.com> --- src/NHibernate/Driver/DriverBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NHibernate/Driver/DriverBase.cs b/src/NHibernate/Driver/DriverBase.cs index abf48081196..935763c7666 100644 --- a/src/NHibernate/Driver/DriverBase.cs +++ b/src/NHibernate/Driver/DriverBase.cs @@ -345,7 +345,7 @@ public void PrepareBatch(DbBatch batch) /// Parameters have been bound by this point, so their order can be adjusted too. /// This is analogous to the RegisterResultSetOutParameter() function in Hibernate. /// - protected virtual void OnBeforePrepare(DbBatch command) + protected virtual void OnBeforePrepare(DbBatch batch) { } From 0205ecc96bca95abf1107513de7f5952b98757d0 Mon Sep 17 00:00:00 2001 From: Gunnar Liljas Date: Wed, 8 Jan 2025 09:55:18 +0100 Subject: [PATCH 12/12] NotSupportedException --- src/NHibernate/Driver/IDriver.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/NHibernate/Driver/IDriver.cs b/src/NHibernate/Driver/IDriver.cs index 06ebd52d0d6..e9eb5e30507 100644 --- a/src/NHibernate/Driver/IDriver.cs +++ b/src/NHibernate/Driver/IDriver.cs @@ -169,9 +169,9 @@ public interface IDriver /// Create a /// /// - /// - DbBatch CreateBatch() => throw new NotImplementedException(); - + /// + DbBatch CreateBatch() => throw new NotSupportedException(); + /// /// Can this driver create es? /// @@ -182,7 +182,7 @@ public interface IDriver /// May be a no-op if the driver does not support preparing commands, or for any other reason. /// /// The batch. - void PrepareBatch(DbBatch dbBatch) => throw new NotImplementedException(); + void PrepareBatch(DbBatch dbBatch) { } /// /// Creates (clones) a from a , @@ -193,8 +193,8 @@ public interface IDriver /// /// /// - /// - DbBatchCommand CreateDbBatchCommandFromDbCommand(DbBatch dbBatch, DbCommand dbCommand) => throw new NotImplementedException(); + /// + DbBatchCommand CreateDbBatchCommandFromDbCommand(DbBatch dbBatch, DbCommand dbCommand) => throw new NotSupportedException(); #endif } }