Skip to content

Commit ba5563b

Browse files
committed
Add support for analyzing plain ADO.NET exceptions
1 parent b922986 commit ba5563b

File tree

50 files changed

+455
-305
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+455
-305
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System.Data.Common;
2+
3+
namespace DbExceptionClassifier.Common
4+
{
5+
public interface IDbExceptionClassifier
6+
{
7+
public bool IsReferenceConstraintError(DbException exception);
8+
public bool IsCannotInsertNullError(DbException exception);
9+
public bool IsNumericOverflowError(DbException exception);
10+
public bool IsUniqueConstraintError(DbException exception);
11+
public bool IsMaxLengthExceededError(DbException exception);
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<Project>
2+
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
3+
4+
<PropertyGroup Label="Configure assembly names and namespaces">
5+
<AssemblyName>DbExceptionClassifier.$(MSBuildProjectName)</AssemblyName>
6+
<RootNamespace>$(AssemblyName)</RootNamespace>
7+
<PackageId>DbExceptionClassifier.$(MSBuildProjectName)</PackageId>
8+
</PropertyGroup>
9+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
10+
<DefineConstants>TRACE;POMELO</DefineConstants>
11+
</PropertyGroup>
12+
13+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
14+
<DefineConstants>TRACE;POMELO</DefineConstants>
15+
</PropertyGroup>
16+
17+
<ItemGroup>
18+
<Compile Include="..\MySQL\MySQLExceptionClassifier.cs" Link="MySQLExceptionClassifier.cs" />
19+
</ItemGroup>
20+
21+
<ItemGroup>
22+
<PackageReference Include="MySqlConnector" Version="2.4.0" />
23+
</ItemGroup>
24+
25+
</Project>
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="MySql.Data" Version="8.3.0" />
11+
</ItemGroup>
12+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
using System.Data.Common;
2+
using DbExceptionClassifier.Common;
3+
4+
#if POMELO
5+
using MySqlConnector;
6+
namespace DbExceptionClassifier.MySQL.Pomelo;
7+
#else
8+
using MySql.Data.MySqlClient;
9+
namespace DbExceptionClassifier.MySQL;
10+
#endif
11+
12+
13+
public class MySQLExceptionClassifier : IDbExceptionClassifier
14+
{
15+
private static MySqlErrorCode GetErrorCode(DbException dbException)
16+
{
17+
if (dbException is not MySqlException mySqlException)
18+
{
19+
return MySqlErrorCode.None;
20+
}
21+
22+
#if POMELO
23+
return mySqlException.ErrorCode;
24+
#else
25+
return (MySqlErrorCode)mySqlException.Number;
26+
#endif
27+
}
28+
29+
public bool IsReferenceConstraintError(DbException exception)
30+
{
31+
var errorCode = GetErrorCode(exception);
32+
33+
return errorCode is MySqlErrorCode.NoReferencedRow or
34+
MySqlErrorCode.RowIsReferenced or
35+
MySqlErrorCode.NoReferencedRow2 or
36+
MySqlErrorCode.RowIsReferenced2;
37+
}
38+
39+
public bool IsCannotInsertNullError(DbException exception)
40+
{
41+
var errorCode = GetErrorCode(exception);
42+
return errorCode == MySqlErrorCode.ColumnCannotBeNull;
43+
}
44+
45+
public bool IsNumericOverflowError(DbException exception)
46+
{
47+
var errorCode = GetErrorCode(exception);
48+
return errorCode == MySqlErrorCode.WarningDataOutOfRange;
49+
}
50+
51+
public bool IsUniqueConstraintError(DbException exception)
52+
{
53+
var errorCode = GetErrorCode(exception);
54+
return errorCode == MySqlErrorCode.DuplicateKeyEntry;
55+
}
56+
57+
public bool IsMaxLengthExceededError(DbException exception)
58+
{
59+
var errorCode = GetErrorCode(exception);
60+
return errorCode == MySqlErrorCode.DataTooLong;
61+
}
62+
}
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Oracle.ManagedDataAccess.Core" Version="3.21.120" />
11+
</ItemGroup>
12+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System.Data.Common;
2+
using DbExceptionClassifier.Common;
3+
using Oracle.ManagedDataAccess.Client;
4+
5+
namespace DbExceptionClassifier.Oracle;
6+
7+
public class OracleExceptionClassifier : IDbExceptionClassifier
8+
{
9+
private const int CannotInsertNull = 1400;
10+
private const int CannotUpdateToNull = 1407;
11+
private const int UniqueConstraintViolation = 1;
12+
private const int IntegrityConstraintViolation = 2291;
13+
private const int ChildRecordFound = 2292;
14+
private const int NumericOverflow = 1438;
15+
private const int NumericOrValueError = 12899;
16+
17+
public bool IsReferenceConstraintError(DbException exception) => exception is OracleException { Number: IntegrityConstraintViolation or ChildRecordFound };
18+
19+
public bool IsCannotInsertNullError(DbException exception) => exception is OracleException { Number: CannotInsertNull or CannotUpdateToNull };
20+
21+
public bool IsNumericOverflowError(DbException exception) => exception is OracleException { Number: NumericOverflow };
22+
23+
public bool IsUniqueConstraintError(DbException exception) => exception is OracleException { Number: UniqueConstraintViolation };
24+
25+
public bool IsMaxLengthExceededError(DbException exception) => exception is OracleException { Number: NumericOrValueError };
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Npgsql" Version="8.0.6" />
11+
</ItemGroup>
12+
13+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using DbExceptionClassifier.Common;
2+
using Npgsql;
3+
using System.Data.Common;
4+
5+
namespace DbExceptionClassifier.PostgreSQL;
6+
7+
public class PostgreSQLExceptionClassifier : IDbExceptionClassifier
8+
{
9+
public bool IsReferenceConstraintError(DbException exception) => exception is PostgresException { SqlState: PostgresErrorCodes.ForeignKeyViolation };
10+
public bool IsCannotInsertNullError(DbException exception) => exception is PostgresException { SqlState: PostgresErrorCodes.NotNullViolation };
11+
public bool IsNumericOverflowError(DbException exception) => exception is PostgresException { SqlState: PostgresErrorCodes.NumericValueOutOfRange };
12+
public bool IsUniqueConstraintError(DbException exception) => exception is PostgresException { SqlState: PostgresErrorCodes.UniqueViolation };
13+
public bool IsMaxLengthExceededError(DbException exception) => exception is PostgresException { SqlState: PostgresErrorCodes.StringDataRightTruncation };
14+
}
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<PropertyGroup>
10+
<BuildType>ManagedOnly</BuildType>
11+
<NoNativeText>This package does not include a copy of the native SQLite library.</NoNativeText>
12+
</PropertyGroup>
13+
<PropertyGroup Condition="'$(BuildType)' == 'ManagedOnly' ">
14+
<PackageId>$(PackageId).Core</PackageId>
15+
<Description>$(Description)$([System.Environment]::NewLine)$([System.Environment]::NewLine)$(NoNativeText)</Description>
16+
</PropertyGroup>
17+
18+
<ItemGroup Condition="'$(BuildType)' == 'Full' ">
19+
<PackageReference Include="Microsoft.Data.SQLite" Version="8.0.0" />
20+
</ItemGroup>
21+
22+
<ItemGroup Condition="'$(BuildType)' == 'ManagedOnly' ">
23+
<PackageReference Include="Microsoft.Data.SQLite.Core" Version="8.0.0" />
24+
</ItemGroup>
25+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using DbExceptionClassifier.Common;
2+
using Microsoft.Data.Sqlite;
3+
using System.Data.Common;
4+
using static SQLitePCL.raw;
5+
6+
namespace DbExceptionClassifier.Sqlite;
7+
8+
public class SqliteExceptionClassifier : IDbExceptionClassifier
9+
{
10+
public bool IsReferenceConstraintError(DbException exception) => exception is SqliteException { SqliteExtendedErrorCode: SQLITE_CONSTRAINT_FOREIGNKEY };
11+
12+
public bool IsCannotInsertNullError(DbException exception) => exception is SqliteException { SqliteExtendedErrorCode: SQLITE_CONSTRAINT_NOTNULL };
13+
14+
public bool IsNumericOverflowError(DbException exception) => false;
15+
16+
public bool IsUniqueConstraintError(DbException exception) => exception is SqliteException
17+
{
18+
SqliteExtendedErrorCode: SQLITE_CONSTRAINT_UNIQUE or SQLITE_CONSTRAINT_PRIMARYKEY
19+
};
20+
21+
public bool IsMaxLengthExceededError(DbException exception) => exception is SqliteException { SqliteExtendedErrorCode: SQLITE_TOOBIG };
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.2" />
11+
</ItemGroup>
12+
13+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using DbExceptionClassifier.Common;
2+
using Microsoft.Data.SqlClient;
3+
using System.Data.Common;
4+
5+
namespace DbExceptionClassifier.SqlServer;
6+
7+
public class SqlServerExceptionClassifier : IDbExceptionClassifier
8+
{
9+
private const int ReferenceConstraint = 547;
10+
private const int CannotInsertNull = 515;
11+
private const int CannotInsertDuplicateKeyUniqueIndex = 2601;
12+
private const int CannotInsertDuplicateKeyUniqueConstraint = 2627;
13+
private const int ArithmeticOverflow = 8115;
14+
private const int StringOrBinaryDataWouldBeTruncated = 8152;
15+
16+
//SQL Server 2019 added a new error with better error message: https://docs.microsoft.com/en-us/archive/blogs/sql_server_team/string-or-binary-data-would-be-truncated-replacing-the-infamous-error-8152
17+
private const int StringOrBinaryDataWouldBeTruncated2019 = 2628;
18+
19+
public bool IsReferenceConstraintError(DbException exception) => exception is SqlException { Number: ReferenceConstraint };
20+
public bool IsCannotInsertNullError(DbException exception) => exception is SqlException { Number: CannotInsertNull };
21+
public bool IsNumericOverflowError(DbException exception) => exception is SqlException { Number: ArithmeticOverflow };
22+
public bool IsUniqueConstraintError(DbException exception) => exception is SqlException { Number: CannotInsertDuplicateKeyUniqueConstraint or CannotInsertDuplicateKeyUniqueIndex };
23+
public bool IsMaxLengthExceededError(DbException exception) => exception is SqlException { Number: StringOrBinaryDataWouldBeTruncated or StringOrBinaryDataWouldBeTruncated2019 };
24+
}

Directory.Build.props

+8-14
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
<Project>
22
<PropertyGroup>
3-
<TargetFrameworks>net8.0</TargetFrameworks>
4-
</PropertyGroup>
5-
6-
<PropertyGroup Label="Configure assembly names and namespaces">
7-
<AssemblyName>$(SolutionName).$(MSBuildProjectName)</AssemblyName>
8-
<RootNamespace>$(AssemblyName)</RootNamespace>
3+
<TargetFramework>net8.0</TargetFramework>
94
</PropertyGroup>
105

116
<PropertyGroup Label="Common assembly attributes">
@@ -16,7 +11,6 @@
1611
<Copyright>Copyright (c) 2018 - 2024 Giorgi Dalakishvili</Copyright>
1712
<IsTrimmable>true</IsTrimmable>
1813

19-
<PackageId>EntityFrameworkCore.Exceptions.$(MSBuildProjectName)</PackageId>
2014
<PackageIcon>Icon.png</PackageIcon>
2115
<PackageReadmeFile>README.md</PackageReadmeFile>
2216
<PackageLicenseFile>License.md</PackageLicenseFile>
@@ -35,16 +29,20 @@
3529

3630
</PropertyGroup>
3731

32+
<ItemGroup Condition="$(MSBuildProjectName) != 'Common'">
33+
<ProjectReference Include="..\Common\Common.csproj" />
34+
</ItemGroup>
35+
3836
<ItemGroup>
39-
<None Include="..\Icon.png">
37+
<None Include="..\..\Icon.png">
4038
<Pack>True</Pack>
4139
<PackagePath></PackagePath>
4240
</None>
43-
<None Include="..\License.md">
41+
<None Include="..\..\License.md">
4442
<Pack>True</Pack>
4543
<PackagePath></PackagePath>
4644
</None>
47-
<None Include="..\README.md">
45+
<None Include="..\..\README.md">
4846
<Pack>True</Pack>
4947
<PackagePath></PackagePath>
5048
</None>
@@ -53,8 +51,4 @@
5351
<ItemGroup>
5452
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
5553
</ItemGroup>
56-
57-
<ItemGroup Condition="$(MSBuildProjectName) != 'Common'">
58-
<ProjectReference Include="..\EntityFramework.Exceptions.Common\Common.csproj" />
59-
</ItemGroup>
6054
</Project>

EntityFramework.Exceptions.MySQL/MySqlExceptionProcessorInterceptor.cs

-44
This file was deleted.

0 commit comments

Comments
 (0)