11. Deleting Data (DELETE)
- Deleting Data (DELETE)
About this chapter
Safely remove resources using DELETE endpoints with proper validation, dependency checking, and cascade delete strategies.
- DELETE endpoints: Removing resources by ID
- HTTP 204 No Content: Standard successful deletion response
- HTTP 404 NotFound: When resource doesn’t exist
- Dependency validation: Checking for related records before deletion
- HTTP 409 Conflict: When deletion violates business rules
- Cascade delete behavior: Automatic cleanup of related entities
Learning outcomes:
- Create DELETE endpoints with proper 404 handling
- Validate dependencies before allowing deletion
- Return appropriate HTTP status codes (204, 404, 409)
- Implement cascade delete strategies in EF Core
- Handle soft deletes vs hard deletes
- Prevent orphaned data in related tables
11.1 DELETE Endpoints
[HttpDelete("{id}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status409Conflict)] // If has dependencies
public async Task<ActionResult> DeletePlatform(int id)
{
var platformFromRepo = await _repository.GetPlatformByIdAsync(id);
if (platformFromRepo == null)
{
return NotFound(new { message = $"Platform with ID {id} not found" });
}
// Optional: Check for dependent records
var commandCount = await _repository.GetPlatformCommandCountAsync(id);
if (commandCount > 0)
{
return Conflict(new
{
message = "Cannot delete platform with existing commands",
commandCount = commandCount
});
}
_repository.DeletePlatform(platformFromRepo); a
wait _repository.SaveChangesAsync();
return NoContent(); // 204}
11.2 Cascade Delete Considerations
// In OnModelCreating
modelBuilder.Entity<Platform>()
.HasMany(p => p.Commands)
.WithOne(c => c.Platform)
.HasForeignKey(c => c.PlatformId)
.OnDelete(DeleteBehavior.Cascade); // Delete all commands when platform deleted
- Delete Behaviors:
- Cascade: Automatically delete children (use with caution!)
- Restrict: Prevent delete if children exist (safer)
- SetNull: Set FK to null (requires nullable FK)
- Business Logic: Sometimes better to check manually
11.3 Soft Delete vs Hard Delete
// Soft Delete: Add IsDeleted column
public class Platform
{
public int Id { get; set; }
public string PlatformName { get; set; }
public bool IsDeleted { get; set; }
public DateTime? DeletedAt { get; set; }
}
// Soft delete implementation
[HttpDelete("{id}")]
public async Task<ActionResult> SoftDeletePlatform(int id)
{
var platform = await _repository.GetPlatformByIdAsync(id);
if (platform == null || platform.IsDeleted)
{
return NotFound();
}
platform.IsDeleted = true;
platform.DeletedAt = DateTime.UtcNow;
await _repository.UpdatePlatformAsync(platform);
await _repository.SaveChangesAsync();
return NoContent();
}
// Global query filter to exclude soft-deleted
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Platform>()
.HasQueryFilter(p => !p.IsDeleted);
}
- When to Use Soft Delete:
- Audit requirements
- Data recovery needs
- Referential integrity
- Regulatory compliance
- Trade-offs: More complex queries, disk space
11.4 Returning Appropriate Status Codes
| Scenario | Status Code | When to Use |
|---|---|---|
| Successfully deleted | 204 No Content | Standard case |
| Already deleted (idempotent) | 204 No Content | DELETE is idempotent |
| Resource not found | 404 Not Found | Never existed or already deleted |
| Cannot delete (dependencies) | 409 Conflict | Business rule violation |
| Unauthorized | 401/403 | Permission issue |