11. Deleting Data (DELETE)

  1. 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