Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace DbExceptionClassifier.PostgreSQL;

public class PostgreSQLExceptionClassifier : IDbExceptionClassifier
{
public bool IsReferenceConstraintError(DbException exception) => exception is PostgresException { SqlState: PostgresErrorCodes.ForeignKeyViolation };
public bool IsReferenceConstraintError(DbException exception) => exception is PostgresException { SqlState: PostgresErrorCodes.ForeignKeyViolation or PostgresErrorCodes.RestrictViolation };
public bool IsCannotInsertNullError(DbException exception) => exception is PostgresException { SqlState: PostgresErrorCodes.NotNullViolation };
public bool IsNumericOverflowError(DbException exception) => exception is PostgresException { SqlState: PostgresErrorCodes.NumericValueOutOfRange };
public bool IsUniqueConstraintError(DbException exception) => exception is PostgresException { SqlState: PostgresErrorCodes.UniqueViolation };
Expand Down
47 changes: 47 additions & 0 deletions EntityFramework.Exceptions/Tests/DatabaseTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,53 @@ await DemoContext.Products
}
}

[Fact]
public virtual async Task DeleteParentItemWithRestrictThrowsReferenceConstraintException()
{
var product = new Product { Name = "AN3" };
var productReview = new ProductReview { Product = product, Comment = "Great" };
DemoContext.ProductReviews.Add(productReview);
await DemoContext.SaveChangesAsync();

CleanupContext();

product = DemoContext.Products.Find(product.Id);
DemoContext.Products.Remove(product);

Assert.Throws<ReferenceConstraintException>(() => DemoContext.SaveChanges());
await Assert.ThrowsAsync<ReferenceConstraintException>(() => DemoContext.SaveChangesAsync());
}

[Fact]
public virtual async Task DeleteParentItemWithRestrictThrowsReferenceConstraintExceptionThroughExecuteDelete()
{
var product = new Product { Name = "AN4" };
var productReview = new ProductReview { Product = product, Comment = "Great" };
DemoContext.ProductReviews.Add(productReview);
await DemoContext.SaveChangesAsync();

CleanupContext();

Assert.Throws<ReferenceConstraintException>(Query);
await Assert.ThrowsAsync<ReferenceConstraintException>(QueryAsync);

return;

void Query()
{
DemoContext.Products
.Where(p => p.Name == "AN4")
.ExecuteDelete();
}

async Task QueryAsync()
{
await DemoContext.Products
.Where(p => p.Name == "AN4")
.ExecuteDeleteAsync();
}
}

[Fact]
public async Task NotHandledViolationReThrowsOriginalException()
{
Expand Down
10 changes: 10 additions & 0 deletions EntityFramework.Exceptions/Tests/DemoContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class DemoContext : DbContext
public DbSet<Product> Products { get; set; }
public DbSet<ProductSale> ProductSales { get; set; }
public DbSet<ProductPriceHistory> ProductPriceHistories { get; set; }
public DbSet<ProductReview> ProductReviews { get; set; }

protected override void OnModelCreating(ModelBuilder builder)
{
Expand All @@ -21,6 +22,7 @@ protected override void OnModelCreating(ModelBuilder builder)
builder.Entity<ProductPriceHistory>().Property(b => b.Price).HasColumnType("decimal(5,2)").IsRequired();
builder.Entity<ProductPriceHistory>().Property(p => p.EffectiveDate).IsRequired();
builder.Entity<ProductPriceHistory>().HasOne(p => p.Product).WithMany().OnDelete(DeleteBehavior.NoAction);
builder.Entity<ProductReview>().HasOne(p => p.Product).WithMany().OnDelete(DeleteBehavior.Restrict);
}

public const int ProductNameMaxLength = 25;
Expand Down Expand Up @@ -50,6 +52,14 @@ public class ProductPriceHistory
public Product Product { get; set; }
}

public class ProductReview
{
public int Id { get; set; }
public string Comment { get; set; }
public int ProductId { get; set; }
public Product Product { get; set; }
}

public class Customer
{
public int Id { get; set; }
Expand Down
2 changes: 1 addition & 1 deletion EntityFramework.Exceptions/Tests/PostgreSQLTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class PostgreSQLDemoContextFixture : DemoContextFixture<PostgreSqlContain
{
static PostgreSQLDemoContextFixture()
{
Container = new PostgreSqlBuilder().Build();
Container = new PostgreSqlBuilder().WithImage("postgres:18").Build();
}

protected override DbContextOptionsBuilder<DemoContext> BuildDemoContextOptions(DbContextOptionsBuilder<DemoContext> builder, string connectionString)
Expand Down
12 changes: 12 additions & 0 deletions EntityFramework.Exceptions/Tests/SqliteTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@ public override Task NumericOverflowViolationThrowsNumericOverflowExceptionThrou
return Task.CompletedTask;
}

[Fact(Skip = "Skipping as SQLite reports RESTRICT violations with SQLITE_CONSTRAINT_TRIGGER instead of SQLITE_CONSTRAINT_FOREIGNKEY")]
public override Task DeleteParentItemWithRestrictThrowsReferenceConstraintException()
{
return Task.CompletedTask;
}

[Fact(Skip = "Skipping as SQLite reports RESTRICT violations with SQLITE_CONSTRAINT_TRIGGER instead of SQLITE_CONSTRAINT_FOREIGNKEY")]
public override Task DeleteParentItemWithRestrictThrowsReferenceConstraintExceptionThroughExecuteDelete()
{
return Task.CompletedTask;
}

[Fact]
public override async Task Deadlock()
{
Expand Down
Loading