|
12 | 12 | using NHibernate.AdoNet; |
13 | 13 | using NHibernate.Cfg; |
14 | 14 | using NUnit.Framework; |
15 | | -using NHibernate.Linq; |
16 | 15 |
|
17 | 16 | namespace NHibernate.Test.Ado |
18 | 17 | { |
@@ -279,44 +278,54 @@ public async Task AbstractBatcherLogFormattedSqlAsync() |
279 | 278 | } |
280 | 279 |
|
281 | 280 | [Test] |
282 | | - [Description("The batcher should handle empty batch execution without throwing exceptions.")] |
283 | | - public async Task EmptyBatchShouldNotThrowExceptionAsync() |
| 281 | + [Description("Inserting exactly BatchSize entities should not throw on commit. See GH-3725.")] |
| 282 | + public async Task InsertExactlyBatchSizeEntitiesShouldNotThrowOnCommitAsync() |
284 | 283 | { |
285 | | - // This test verifies that batchers handle empty batches correctly |
286 | | - // DbBatchBatcher had a bug where ExecuteBatch was called on an empty batch, |
287 | | - // causing InvalidOperationException: CommandText property has not been initialized |
288 | | - // See GH-3725 |
| 284 | + // This test verifies that DbBatchBatcher handles empty batches correctly. |
| 285 | + // The bug (GH-3725): When inserting exactly BatchSize entities, the batch auto-executes |
| 286 | + // when full (via ExecuteBatchWithTiming), which clears _currentBatch but NOT _batchCommand. |
| 287 | + // On commit, ExecuteBatch() is called, sees _batchCommand is set, and calls DoExecuteBatch |
| 288 | + // on an empty _currentBatch, causing InvalidOperationException. |
289 | 289 |
|
290 | | - using var session = OpenSession(); |
291 | | - using var transaction = session.BeginTransaction(); |
| 290 | + // BatchSize is configured as 10 in this fixture |
| 291 | + const int batchSize = 10; |
292 | 292 |
|
293 | | - // Execute queries that don't add to the batch |
294 | | - _ = await (session.Query<VerySimple>().FirstOrDefaultAsync()); |
| 293 | + using (var session = OpenSession()) |
| 294 | + using (var transaction = session.BeginTransaction()) |
| 295 | + { |
| 296 | + // Insert exactly BatchSize entities - this fills the batch and triggers auto-execution |
| 297 | + for (int i = 0; i < batchSize; i++) |
| 298 | + { |
| 299 | + await (session.SaveAsync(new VerySimple { Id = 1000 + i, Name = $"Test{i}", Weight = i * 1.1 })); |
| 300 | + } |
295 | 301 |
|
296 | | - // Prepare a new command which triggers ExecuteBatch on any existing batch |
297 | | - // If the previous command didn't add anything to the batch, this would fail |
298 | | - // before the fix with InvalidOperationException |
299 | | - _ = await (session.Query<VerySimple>().FirstOrDefaultAsync()); |
| 302 | + // Commit triggers ExecuteBatch() which would fail on empty batch without the fix |
| 303 | + await (transaction.CommitAsync()); |
| 304 | + } |
300 | 305 |
|
301 | | - // Test passes if no exception is thrown |
302 | | - await (transaction.CommitAsync()); |
| 306 | + await (CleanupAsync()); |
303 | 307 | } |
304 | 308 |
|
305 | 309 | [Test] |
306 | | - [Description("Flush with no pending operations should handle empty batch correctly.")] |
307 | | - public async Task FlushEmptyBatchShouldNotThrowExceptionAsync() |
| 310 | + [Description("Inserting a multiple of BatchSize entities should not throw on commit. See GH-3725.")] |
| 311 | + public async Task InsertMultipleOfBatchSizeEntitiesShouldNotThrowOnCommitAsync() |
308 | 312 | { |
309 | | - using var session = OpenSession(); |
310 | | - using var transaction = session.BeginTransaction(); |
| 313 | + // Same issue as above but with multiple full batches |
| 314 | + const int batchSize = 10; |
| 315 | + const int multiplier = 3; |
311 | 316 |
|
312 | | - // Query without any modifications |
313 | | - var count = await (session.Query<VerySimple>().CountAsync()); |
314 | | - Assert.That(count, Is.GreaterThanOrEqualTo(0)); |
| 317 | + using (var session = OpenSession()) |
| 318 | + using (var transaction = session.BeginTransaction()) |
| 319 | + { |
| 320 | + for (int i = 0; i < batchSize * multiplier; i++) |
| 321 | + { |
| 322 | + await (session.SaveAsync(new VerySimple { Id = 2000 + i, Name = $"Test{i}", Weight = i * 1.1 })); |
| 323 | + } |
315 | 324 |
|
316 | | - // Flush with no pending batch operations should not throw |
317 | | - Assert.DoesNotThrowAsync(() => session.FlushAsync()); |
| 325 | + await (transaction.CommitAsync()); |
| 326 | + } |
318 | 327 |
|
319 | | - await (transaction.CommitAsync()); |
| 328 | + await (CleanupAsync()); |
320 | 329 | } |
321 | 330 | } |
322 | 331 | } |
0 commit comments