Skip to content

Commit 377a3b3

Browse files
committed
Stop wrapping OperationCancellationException with DbUpdateException
Fixes #15074
1 parent 8ee0305 commit 377a3b3

File tree

4 files changed

+94
-34
lines changed

4 files changed

+94
-34
lines changed

src/EFCore.Relational/Update/AffectedCountModificationCommandBatch.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ protected override void Consume(RelationalDataReader reader)
8484
"Expected " + expectedResultSetCount + " result sets, got " + actualResultSetCount);
8585
#endif
8686
}
87-
catch (Exception ex) when (!(ex is DbUpdateException))
87+
catch (Exception ex) when (ex is not DbUpdateException and not OperationCanceledException)
8888
{
8989
throw new DbUpdateException(
9090
RelationalStrings.UpdateStoreException,
@@ -150,7 +150,7 @@ protected override async Task ConsumeAsync(
150150
"Expected " + expectedResultSetCount + " result sets, got " + actualResultSetCount);
151151
#endif
152152
}
153-
catch (Exception ex) when (!(ex is DbUpdateException))
153+
catch (Exception ex) when (ex is not DbUpdateException and not OperationCanceledException)
154154
{
155155
throw new DbUpdateException(
156156
RelationalStrings.UpdateStoreException,

src/EFCore.Relational/Update/ReaderModificationCommandBatch.cs

+2-10
Original file line numberDiff line numberDiff line change
@@ -252,11 +252,7 @@ public override void Execute(IRelationalConnection connection)
252252
Dependencies.Logger, CommandSource.SaveChanges));
253253
Consume(dataReader);
254254
}
255-
catch (DbUpdateException)
256-
{
257-
throw;
258-
}
259-
catch (Exception ex)
255+
catch (Exception ex) when (ex is not DbUpdateException and not OperationCanceledException)
260256
{
261257
throw new DbUpdateException(
262258
RelationalStrings.UpdateStoreException,
@@ -293,11 +289,7 @@ public override async Task ExecuteAsync(
293289
cancellationToken).ConfigureAwait(false);
294290
await ConsumeAsync(dataReader, cancellationToken).ConfigureAwait(false);
295291
}
296-
catch (DbUpdateException)
297-
{
298-
throw;
299-
}
300-
catch (Exception ex)
292+
catch (Exception ex) when (ex is not DbUpdateException and not OperationCanceledException)
301293
{
302294
throw new DbUpdateException(
303295
RelationalStrings.UpdateStoreException,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Data.Common;
5+
6+
namespace Microsoft.EntityFrameworkCore.TestUtilities.FakeProvider
7+
{
8+
public class FakeDbException : DbException
9+
{
10+
}
11+
}

test/EFCore.Relational.Tests/Update/ReaderModificationCommandBatchTest.cs

+79-22
Original file line numberDiff line numberDiff line change
@@ -261,8 +261,10 @@ public async Task Exception_not_thrown_for_more_than_one_row_returned_for_single
261261
Assert.Equal(42, entry[entry.EntityType.FindProperty("Id")]);
262262
}
263263

264-
[ConditionalFact]
265-
public async Task Exception_thrown_if_rows_returned_for_command_without_store_generated_values_is_not_1()
264+
[ConditionalTheory]
265+
[InlineData(false)]
266+
[InlineData(true)]
267+
public async Task Exception_thrown_if_rows_returned_for_command_without_store_generated_values_is_not_1(bool async)
266268
{
267269
var entry = CreateEntry(EntityState.Added);
268270

@@ -276,14 +278,17 @@ public async Task Exception_thrown_if_rows_returned_for_command_without_store_ge
276278
var batch = new ModificationCommandBatchFake();
277279
batch.AddCommand(command);
278280

279-
Assert.Equal(
280-
RelationalStrings.UpdateConcurrencyException(1, 42),
281-
(await Assert.ThrowsAsync<DbUpdateConcurrencyException>(
282-
async () => await batch.ExecuteAsync(connection))).Message);
281+
var exception = async
282+
? await Assert.ThrowsAsync<DbUpdateConcurrencyException>(() => batch.ExecuteAsync(connection))
283+
: Assert.Throws<DbUpdateConcurrencyException>(() => batch.Execute(connection));
284+
285+
Assert.Equal(RelationalStrings.UpdateConcurrencyException(1, 42), exception.Message);
283286
}
284287

285-
[ConditionalFact]
286-
public async Task Exception_thrown_if_no_rows_returned_for_command_with_store_generated_values()
288+
[ConditionalTheory]
289+
[InlineData(false)]
290+
[InlineData(true)]
291+
public async Task Exception_thrown_if_no_rows_returned_for_command_with_store_generated_values(bool async)
287292
{
288293
var entry = CreateEntry(EntityState.Added, generateKeyValues: true);
289294
entry.SetTemporaryValue(entry.EntityType.FindPrimaryKey().Properties[0], -1);
@@ -297,10 +302,65 @@ public async Task Exception_thrown_if_no_rows_returned_for_command_with_store_ge
297302
var batch = new ModificationCommandBatchFake();
298303
batch.AddCommand(command);
299304

300-
Assert.Equal(
301-
RelationalStrings.UpdateConcurrencyException(1, 0),
302-
(await Assert.ThrowsAsync<DbUpdateConcurrencyException>(
303-
async () => await batch.ExecuteAsync(connection))).Message);
305+
var exception = async
306+
? await Assert.ThrowsAsync<DbUpdateConcurrencyException>(() => batch.ExecuteAsync(connection))
307+
: Assert.Throws<DbUpdateConcurrencyException>(() => batch.Execute(connection));
308+
309+
Assert.Equal(RelationalStrings.UpdateConcurrencyException(1, 0), exception.Message);
310+
}
311+
312+
[ConditionalTheory]
313+
[InlineData(false)]
314+
[InlineData(true)]
315+
public async Task DbException_is_wrapped_with_DbUpdateException(bool async)
316+
{
317+
var entry = CreateEntry(EntityState.Added, generateKeyValues: true);
318+
319+
var command = CreateModificationCommand("T1", null, new ParameterNameGenerator().GenerateNext, true, null);
320+
command.AddEntry(entry, true);
321+
322+
var originalException = new FakeDbException();
323+
324+
var connection = CreateConnection(
325+
new FakeCommandExecutor(
326+
executeReaderAsync: (c, b, ct) => throw originalException,
327+
executeReader: (c, b) => throw originalException));
328+
329+
var batch = new ModificationCommandBatchFake();
330+
batch.AddCommand(command);
331+
332+
var actualException = async
333+
? await Assert.ThrowsAsync<DbUpdateException>(() => batch.ExecuteAsync(connection))
334+
: Assert.Throws<DbUpdateException>(() => batch.Execute(connection));
335+
336+
Assert.Same(originalException, actualException.InnerException);
337+
}
338+
339+
[ConditionalTheory]
340+
[InlineData(false)]
341+
[InlineData(true)]
342+
public async Task OperationCanceledException_is_not_wrapped_with_DbUpdateException(bool async)
343+
{
344+
var entry = CreateEntry(EntityState.Added, generateKeyValues: true);
345+
346+
var command = CreateModificationCommand("T1", null, new ParameterNameGenerator().GenerateNext, true, null);
347+
command.AddEntry(entry, true);
348+
349+
var originalException = new OperationCanceledException();
350+
351+
var connection = CreateConnection(
352+
new FakeCommandExecutor(
353+
executeReaderAsync: (c, b, ct) => throw originalException,
354+
executeReader: (c, b) => throw originalException));
355+
356+
var batch = new ModificationCommandBatchFake();
357+
batch.AddCommand(command);
358+
359+
var actualException = async
360+
? await Assert.ThrowsAsync<OperationCanceledException>(() => batch.ExecuteAsync(connection))
361+
: Assert.Throws<OperationCanceledException>(() => batch.Execute(connection));
362+
363+
Assert.Same(originalException, actualException);
304364
}
305365

306366
[ConditionalFact]
@@ -605,21 +665,18 @@ private class FakeDbContext : DbContext
605665

606666
private const string ConnectionString = "Fake Connection String";
607667

668+
private static FakeRelationalConnection CreateConnection(FakeCommandExecutor executor)
669+
=> CreateConnection(
670+
CreateOptions(
671+
new FakeRelationalOptionsExtension().WithConnection(
672+
new FakeDbConnection(ConnectionString, executor))));
673+
608674
private static FakeRelationalConnection CreateConnection(DbDataReader dbDataReader)
609-
{
610-
var fakeDbConnection = new FakeDbConnection(
611-
ConnectionString,
675+
=> CreateConnection(
612676
new FakeCommandExecutor(
613677
executeReaderAsync: (c, b, ct) => Task.FromResult(dbDataReader),
614678
executeReader: (c, b) => dbDataReader));
615679

616-
var optionsExtension = new FakeRelationalOptionsExtension().WithConnection(fakeDbConnection);
617-
618-
var options = CreateOptions(optionsExtension);
619-
620-
return CreateConnection(options);
621-
}
622-
623680
private static FakeRelationalConnection CreateConnection(IDbContextOptions options = null)
624681
=> new(options ?? CreateOptions());
625682

0 commit comments

Comments
 (0)