Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion EntityFramework.Exceptions/Tests/PostgreSQLTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using EntityFramework.Exceptions.PostgreSQL;
using EntityFramework.Exceptions.Common;
using EntityFramework.Exceptions.PostgreSQL;
using Microsoft.EntityFrameworkCore;
using System.Threading.Tasks;
using Testcontainers.PostgreSql;
using Xunit;

Expand All @@ -10,6 +12,19 @@ public class PostgreSQLTests : DatabaseTests, IClassFixture<PostgreSQLDemoContex
public PostgreSQLTests(PostgreSQLDemoContextFixture fixture) : base(fixture.DemoContext, fixture.SameNameIndexesContext)
{
}

[Fact]
public async Task MaxLengthViolationThrowsMaxLengthExceededExceptionThroughRawSql()
{
var longName = new string('G', DemoContext.ProductNameMaxLength + 5);

Assert.Throws<MaxLengthExceededException>(() =>
DemoContext.Database.ExecuteSqlInterpolated(
$"INSERT INTO \"Products\" (\"Name\") VALUES ({longName})"));
await Assert.ThrowsAsync<MaxLengthExceededException>(() =>
DemoContext.Database.ExecuteSqlInterpolatedAsync(
$"INSERT INTO \"Products\" (\"Name\") VALUES ({longName})"));
}
}

public class PostgreSQLDemoContextFixture : DemoContextFixture<PostgreSqlContainer>
Expand Down
51 changes: 51 additions & 0 deletions claude.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# EntityFramework.Exceptions

Typed exception handling for Entity Framework Core. Converts database-specific errors into strongly-typed .NET exceptions instead of generic `DbUpdateException`.

## Build & Test

```bash
dotnet restore # Restore NuGet dependencies
dotnet build --no-restore # Build all projects
dotnet test --no-restore # Run tests (requires Docker for Testcontainers)
```

Tests use **Testcontainers** and require a running Docker daemon for containerized providers (SQL Server, PostgreSQL, MySQL, Oracle), each of which spins up its own container. **SQLite** tests run in-process and do not require Docker.

## Project Structure

The solution (`EntityFramework.Exceptions.slnx`) has two main layers:

- **DbExceptionClassifier/** — Database-specific error code classification. Each provider implements `IDbExceptionClassifier` to map native error codes to a `DatabaseError` enum.
- `Common/` — `IDbExceptionClassifier` interface
- `SqlServer/`, `PostgreSQL/`, `MySQL/`, `MySQL.Pomelo/`, `Oracle/`, `Sqlite/` — Provider implementations

- **EntityFramework.Exceptions/** — EF Core integration via interceptors. Catches `DbException`, classifies it, and throws a typed exception.
- `Common/` — Base `ExceptionProcessorInterceptor<T>`, exception classes (`UniqueConstraintException`, `CannotInsertNullException`, `MaxLengthExceededException`, `NumericOverflowException`, `ReferenceConstraintException`), `ExceptionFactory`
- `SqlServer/`, `PostgreSQL/`, `MySQL/`, `MySQL.Pomelo/`, `Oracle/`, `Sqlite/` — Provider-specific interceptors and `UseExceptionProcessor()` extension methods
- `Tests/` — xUnit test suite using Testcontainers

- **Directory.Build.props** — Shared build properties (target framework, version, NuGet metadata). All non-Common projects automatically reference their corresponding Common project.

## Architecture

1. **Interceptor pattern**: `ExceptionProcessorInterceptor<TProviderException>` implements `IDbCommandInterceptor` and `ISaveChangesInterceptor`
2. **Classification**: Each database provider has an `IDbExceptionClassifier` that maps native error codes to `DatabaseError` enum values
3. **Factory**: `ExceptionFactory` creates the appropriate typed exception
4. **Extension methods**: Each provider exposes `UseExceptionProcessor()` on `DbContextOptionsBuilder`

## Code Conventions

- **C# / .NET 8.0** with file-scoped namespaces, primary constructors, nullable reference types, and implicit usings
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The statement mentions "implicit usings" as a code convention, but the test project (Tests.csproj) does not have ImplicitUsings enabled. Evidence: explicit using statements are still required in test files (e.g., System.Threading.Tasks was added in PostgreSQLTests.cs), and the Tests.csproj file doesn't contain an ImplicitUsings property. Consider removing "implicit usings" from this list or clarifying that it applies only to certain projects.

Suggested change
- **C# / .NET 8.0** with file-scoped namespaces, primary constructors, nullable reference types, and implicit usings
- **C# / .NET 8.0** with file-scoped namespaces, primary constructors, nullable reference types; implicit usings are enabled in library projects, while test projects use explicit `using` directives

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The statement mentions "nullable reference types" as a code convention, but none of the csproj files examined (Directory.Build.props, Tests.csproj, PostgreSQL.csproj) contain a Nullable property set to "enable". In .NET 8.0, nullable reference types must be explicitly enabled via the Nullable property in the project file. Consider removing "nullable reference types" from this list or clarifying which projects have this enabled, if any.

Suggested change
- **C# / .NET 8.0** with file-scoped namespaces, primary constructors, nullable reference types, and implicit usings
- **C# / .NET 8.0** with file-scoped namespaces, primary constructors, and implicit usings

Copilot uses AI. Check for mistakes.
- **Naming**: `[Database]ExceptionClassifier`, `[Database]ExceptionProcessorInterceptor`, `ExceptionProcessorExtensions.UseExceptionProcessor()`
- **Test naming**: `[Scenario]Throws[ExceptionType]` (e.g., `UniqueColumnViolationThrowsUniqueConstraintException`)
- **MySQL.Pomelo** shares source files with **MySQL** via `<Link>` in .csproj and uses `#if POMELO` preprocessor directive
- Exception classes follow a standard pattern: inherit `DbUpdateException`, provide all standard constructor overloads, and optionally expose `ConstraintName`, `ConstraintProperties`, and `SchemaQualifiedTableName` properties

## Testing Notes

- Base test class `DatabaseTests` defines ~12 virtual test methods; provider-specific test classes inherit and override as needed
- **SQLite** does not populate `ConstraintName`/`ConstraintProperties` and does not enforce numeric overflow
- **SQL Server** skips numeric overflow tests (`ArgumentException` from SqlClient)
- **MySQL** primary key violations do not populate constraint properties
- Test fixtures use `IAsyncLifetime` for container lifecycle management
Loading