Skip to content

Commit 7b7ca3d

Browse files
authored
Merge pull request #89 from pengweiqhca/main
Add IsDeadlockError
2 parents 0c455bb + 2175fe7 commit 7b7ca3d

File tree

13 files changed

+101
-23
lines changed

13 files changed

+101
-23
lines changed

DbExceptionClassifier/Common/IDbExceptionClassifier.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ public interface IDbExceptionClassifier
99
public bool IsNumericOverflowError(DbException exception);
1010
public bool IsUniqueConstraintError(DbException exception);
1111
public bool IsMaxLengthExceededError(DbException exception);
12+
public bool IsDeadlockError(DbException exception) => false;
1213
}
1314
}

DbExceptionClassifier/MySQL/MySQLExceptionClassifier.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,10 @@ public bool IsMaxLengthExceededError(DbException exception)
5959
var errorCode = GetErrorCode(exception);
6060
return errorCode == MySqlErrorCode.DataTooLong;
6161
}
62-
}
62+
63+
public bool IsDeadlockError(DbException exception)
64+
{
65+
var errorCode = GetErrorCode(exception);
66+
return errorCode is MySqlErrorCode.LockDeadlock or MySqlErrorCode.XARBDeadlock;
67+
}
68+
}

DbExceptionClassifier/Oracle/OracleExceptionClassifier.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public class OracleExceptionClassifier : IDbExceptionClassifier
99
private const int CannotInsertNull = 1400;
1010
private const int CannotUpdateToNull = 1407;
1111
private const int UniqueConstraintViolation = 1;
12+
private const int DeadLock = 60;
1213
private const int IntegrityConstraintViolation = 2291;
1314
private const int ChildRecordFound = 2292;
1415
private const int NumericOverflow = 1438;
@@ -23,4 +24,6 @@ public class OracleExceptionClassifier : IDbExceptionClassifier
2324
public bool IsUniqueConstraintError(DbException exception) => exception is OracleException { Number: UniqueConstraintViolation };
2425

2526
public bool IsMaxLengthExceededError(DbException exception) => exception is OracleException { Number: NumericOrValueError };
26-
}
27+
28+
public bool IsDeadlockError(DbException exception) => exception is OracleException { Number: DeadLock };
29+
}

DbExceptionClassifier/PostgreSQL/PostgreSQLExceptionClassifier.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ public class PostgreSQLExceptionClassifier : IDbExceptionClassifier
1111
public bool IsNumericOverflowError(DbException exception) => exception is PostgresException { SqlState: PostgresErrorCodes.NumericValueOutOfRange };
1212
public bool IsUniqueConstraintError(DbException exception) => exception is PostgresException { SqlState: PostgresErrorCodes.UniqueViolation };
1313
public bool IsMaxLengthExceededError(DbException exception) => exception is PostgresException { SqlState: PostgresErrorCodes.StringDataRightTruncation };
14-
}
14+
public bool IsDeadlockError(DbException exception) => exception is PostgresException { SqlState: PostgresErrorCodes.DeadlockDetected };
15+
}

DbExceptionClassifier/SqlServer/SqlServerExceptionClassifier.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ public class SqlServerExceptionClassifier : IDbExceptionClassifier
88
{
99
private const int ReferenceConstraint = 547;
1010
private const int CannotInsertNull = 515;
11+
private const int Deadlock = 1205;
1112
private const int CannotInsertDuplicateKeyUniqueIndex = 2601;
1213
private const int CannotInsertDuplicateKeyUniqueConstraint = 2627;
1314
private const int ArithmeticOverflow = 8115;
@@ -21,4 +22,5 @@ public class SqlServerExceptionClassifier : IDbExceptionClassifier
2122
public bool IsNumericOverflowError(DbException exception) => exception is SqlException { Number: ArithmeticOverflow };
2223
public bool IsUniqueConstraintError(DbException exception) => exception is SqlException { Number: CannotInsertDuplicateKeyUniqueConstraint or CannotInsertDuplicateKeyUniqueIndex };
2324
public bool IsMaxLengthExceededError(DbException exception) => exception is SqlException { Number: StringOrBinaryDataWouldBeTruncated or StringOrBinaryDataWouldBeTruncated2019 };
24-
}
25+
public bool IsDeadlockError(DbException exception) => exception is SqlException { Number: Deadlock };
26+
}

DbExceptionClassifier/Sqlite/SqliteExceptionClassifier.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@ public bool IsUniqueConstraintError(DbException exception) => exception is Sqlit
1919
};
2020

2121
public bool IsMaxLengthExceededError(DbException exception) => exception is SqliteException { SqliteExtendedErrorCode: SQLITE_TOOBIG };
22-
}
22+
}

EntityFramework.Exceptions/Common/ExceptionFactory.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ internal static Exception Create<T>(ExceptionProcessorInterceptor<T>.DatabaseErr
1616
ExceptionProcessorInterceptor<T>.DatabaseError.NumericOverflow => new NumericOverflowException("Numeric overflow", exception.InnerException, entries),
1717
ExceptionProcessorInterceptor<T>.DatabaseError.ReferenceConstraint => new ReferenceConstraintException("Reference constraint violation", exception.InnerException, entries),
1818
ExceptionProcessorInterceptor<T>.DatabaseError.UniqueConstraint => new UniqueConstraintException("Unique constraint violation", exception.InnerException, entries),
19+
ExceptionProcessorInterceptor<T>.DatabaseError.DeadLock => new DeadlockException("Deadlock", exception.InnerException, entries),
1920
_ => null,
2021
};
2122
}
22-
}
23+
}

EntityFramework.Exceptions/Common/ExceptionProcessorInterceptor.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ protected internal enum DatabaseError
2323
CannotInsertNull,
2424
MaxLength,
2525
NumericOverflow,
26-
ReferenceConstraint
26+
ReferenceConstraint,
27+
DeadLock,
2728
}
2829

2930
/// <inheritdoc />
@@ -59,6 +60,7 @@ public void CommandFailed(DbCommand command, CommandErrorEventData eventData)
5960
if (exceptionClassifier.IsCannotInsertNullError(dbException)) return DatabaseError.CannotInsertNull;
6061
if (exceptionClassifier.IsUniqueConstraintError(dbException)) return DatabaseError.UniqueConstraint;
6162
if (exceptionClassifier.IsReferenceConstraintError(dbException)) return DatabaseError.ReferenceConstraint;
63+
if (exceptionClassifier.IsDeadlockError(dbException)) return DatabaseError.DeadLock;
6264

6365
return null;
6466
}
@@ -94,9 +96,9 @@ private void SetConstraintDetails(DbContext context, UniqueConstraintException e
9496
{
9597
var indexes = context.Model.GetEntityTypes().SelectMany(x => x.GetDeclaredIndexes().Where(index => index.IsUnique));
9698

97-
var mappedIndexes = indexes.SelectMany(index => index.GetMappedTableIndexes(),
99+
var mappedIndexes = indexes.SelectMany(index => index.GetMappedTableIndexes(),
98100
(index, tableIndex) => new IndexDetails(tableIndex.Name, tableIndex.Table.SchemaQualifiedName, index.Properties));
99-
101+
100102
var primaryKeys = context.Model.GetEntityTypes().SelectMany(x =>
101103
{
102104
var primaryKey = x.FindPrimaryKey();
@@ -152,4 +154,4 @@ private void SetConstraintDetails(DbContext context, ReferenceConstraintExceptio
152154
exception.SchemaQualifiedTableName = match.SchemaQualifiedTableName;
153155
}
154156
}
155-
}
157+
}

EntityFramework.Exceptions/Common/Exceptions.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,4 +127,27 @@ public ReferenceConstraintException(string message, Exception innerException, IR
127127
public string ConstraintName { get; internal set; }
128128
public IReadOnlyList<string> ConstraintProperties { get; internal set; }
129129
public string SchemaQualifiedTableName { get; internal set; }
130-
}
130+
}
131+
132+
public class DeadlockException : DbUpdateException
133+
{
134+
public DeadlockException()
135+
{
136+
}
137+
138+
public DeadlockException(string message) : base(message)
139+
{
140+
}
141+
142+
public DeadlockException(string message, Exception innerException) : base(message, innerException)
143+
{
144+
}
145+
146+
public DeadlockException(string message, IReadOnlyList<EntityEntry> entries) : base(message, entries)
147+
{
148+
}
149+
150+
public DeadlockException(string message, Exception innerException, IReadOnlyList<EntityEntry> entries) : base(message, innerException, entries)
151+
{
152+
}
153+
}

EntityFramework.Exceptions/Tests/DatabaseTests.cs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public virtual async Task UniqueColumnViolationSameNamesIndexesInDifferentSchema
7171
{
7272
Name = "Rope Access"
7373
});
74-
74+
7575
SameNameIndexesContext.IncidentCategories.Add(new EFExceptionSchema.Entities.Incidents.Category
7676
{
7777
Name = "Rope Access"
@@ -109,7 +109,7 @@ public virtual async Task PrimaryKeyViolationThrowsUniqueConstraintException()
109109
Assert.False(string.IsNullOrEmpty(uniqueConstraintException.ConstraintName));
110110
Assert.False(string.IsNullOrEmpty(uniqueConstraintException.SchemaQualifiedTableName));
111111
Assert.NotEmpty(uniqueConstraintException.ConstraintProperties);
112-
Assert.Contains<string>(nameof(Product.Id), uniqueConstraintException.ConstraintProperties);
112+
Assert.Contains<string>(nameof(Product.Id), uniqueConstraintException.ConstraintProperties);
113113
}
114114
}
115115

@@ -348,6 +348,34 @@ public async Task NotHandledViolationReThrowsOriginalException()
348348
await Assert.ThrowsAsync<DbUpdateException>(() => DemoContext.SaveChangesAsync());
349349
}
350350

351+
[Fact]
352+
public virtual async Task Deadlock()
353+
{
354+
var p1 = DemoContext.Products.Add(new() { Name = "Test1" });
355+
var p2 = DemoContext.Products.Add(new() { Name = "Test2" });
356+
357+
await DemoContext.SaveChangesAsync();
358+
359+
var id1 = p1.Entity.Id;
360+
var id2 = p2.Entity.Id;
361+
362+
using var controlContext = new DemoContext(DemoContext.Options);
363+
using var transaction1 = await DemoContext.Database.BeginTransactionAsync();
364+
using var transaction2 = await controlContext.Database.BeginTransactionAsync();
365+
366+
await DemoContext.Products.Where(c => c.Id == id1)
367+
.ExecuteUpdateAsync(c => c.SetProperty(p => p.Name, "Test11"));
368+
369+
await controlContext.Products.Where(c => c.Id == id2)
370+
.ExecuteUpdateAsync(c => c.SetProperty(p => p.Name, "Test21"));
371+
372+
await Assert.ThrowsAsync<DeadlockException>(() => Task.WhenAll(Task.Run(() => DemoContext.Products
373+
.Where(c => c.Id == id2)
374+
.ExecuteUpdateAsync(c => c.SetProperty(p => p.Name, "Test22"))), controlContext.Products
375+
.Where(c => c.Id == id1)
376+
.ExecuteUpdateAsync(c => c.SetProperty(p => p.Name, "Test12"))));
377+
}
378+
351379
public virtual void Dispose()
352380
{
353381
CleanupContext();
@@ -360,4 +388,4 @@ protected void CleanupContext()
360388
entityEntry.State = EntityState.Detached;
361389
}
362390
}
363-
}
391+
}

0 commit comments

Comments
 (0)