|
8 | 8 | //------------------------------------------------------------------------------ |
9 | 9 |
|
10 | 10 |
|
| 11 | +using System.Linq; |
11 | 12 | using NHibernate.AdoNet; |
12 | 13 | using NHibernate.Cfg; |
13 | 14 | using NUnit.Framework; |
@@ -38,11 +39,13 @@ protected override string[] Mappings |
38 | 39 | get { return new[] { "Ado.VerySimple.hbm.xml", "Ado.AlmostSimple.hbm.xml" }; } |
39 | 40 | } |
40 | 41 |
|
| 42 | + private const int BatchSize = 10; |
| 43 | + |
41 | 44 | protected override void Configure(Configuration configuration) |
42 | 45 | { |
43 | 46 | configuration.SetProperty(Environment.FormatSql, "true"); |
44 | 47 | configuration.SetProperty(Environment.GenerateStatistics, "true"); |
45 | | - configuration.SetProperty(Environment.BatchSize, "10"); |
| 48 | + configuration.SetProperty(Environment.BatchSize, BatchSize.ToString()); |
46 | 49 | #if NET6_0_OR_GREATER |
47 | 50 | if (_useDbBatch) |
48 | 51 | { |
@@ -275,5 +278,55 @@ public async Task AbstractBatcherLogFormattedSqlAsync() |
275 | 278 | Assert.That(Sfi.Statistics.PrepareStatementCount, Is.EqualTo(1)); |
276 | 279 | await (CleanupAsync()); |
277 | 280 | } |
| 281 | + |
| 282 | + [Test] |
| 283 | + [Description("Inserting exactly BatchSize entities should not throw on commit. See GH-3725.")] |
| 284 | + public async Task InsertExactlyBatchSizeEntitiesShouldNotThrowOnCommitAsync() |
| 285 | + { |
| 286 | + // This test verifies that DbBatchBatcher handles empty batches correctly. |
| 287 | + // The bug (GH-3725): When inserting exactly BatchSize entities, the batch auto-executes |
| 288 | + // when full (via ExecuteBatchWithTiming), which clears _currentBatch but NOT _batchCommand. |
| 289 | + // On commit, ExecuteBatch() is called, sees _batchCommand is set, and calls DoExecuteBatch |
| 290 | + // on an empty _currentBatch, causing InvalidOperationException. |
| 291 | + |
| 292 | + using (var session = OpenSession()) |
| 293 | + using (var transaction = session.BeginTransaction()) |
| 294 | + { |
| 295 | + // Insert exactly BatchSize entities - this fills the batch and triggers auto-execution. |
| 296 | + for (int i = 0; i < BatchSize; i++) |
| 297 | + { |
| 298 | + await (session.SaveAsync(new VerySimple { Id = 1000 + i, Name = $"Test{i}", Weight = i * 1.1 })); |
| 299 | + } |
| 300 | + |
| 301 | + // Commit triggers ExecuteBatch() which would fail on empty batch without the fix, |
| 302 | + // depending on the driver. It fails with Microsoft.Data.SqlClient by example, not with |
| 303 | + // System.Data.SqlClient. |
| 304 | + await (transaction.CommitAsync()); |
| 305 | + } |
| 306 | + |
| 307 | + await (CleanupAsync()); |
| 308 | + } |
| 309 | + |
| 310 | + [Test] |
| 311 | + [Description("Inserting a multiple of BatchSize entities should not throw on commit. See GH-3725.")] |
| 312 | + public async Task InsertMultipleOfBatchSizeEntitiesShouldNotThrowOnCommitAsync() |
| 313 | + { |
| 314 | + // Same issue as above but with multiple full batches |
| 315 | + const int batchSize = 10; |
| 316 | + const int multiplier = 3; |
| 317 | + |
| 318 | + using (var session = OpenSession()) |
| 319 | + using (var transaction = session.BeginTransaction()) |
| 320 | + { |
| 321 | + for (int i = 0; i < batchSize * multiplier; i++) |
| 322 | + { |
| 323 | + await (session.SaveAsync(new VerySimple { Id = 2000 + i, Name = $"Test{i}", Weight = i * 1.1 })); |
| 324 | + } |
| 325 | + |
| 326 | + await (transaction.CommitAsync()); |
| 327 | + } |
| 328 | + |
| 329 | + await (CleanupAsync()); |
| 330 | + } |
278 | 331 | } |
279 | 332 | } |
0 commit comments