Blog: enforce Restrict FK on BlogPost.Author and Comment.Author

Tout billet a un auteur, tout commentaire a un auteur. On aligne la
base sur ce contrat (Postgres) en droppant les orphelins existants
puis en remplaçant les FK en cascade par des FK Restrict.

- ApplicationDbContext: fluent pour BlogPost.Author et Comment.Author
  en DeleteBehavior.Restrict.
- ApplicationUser: ajoute la nav inverse BlogComments (manquait,
  EF aurait sinon créé une shadow FK).
- Migration 20260711173717_EnforceBlogAuthorFKs: Up purge les
  Comment/BlogSpot dont l'AuthorId n'existe plus, log le volume,
  puis drop+add des FK. Down laisse la cascade (état pré-migration).

Le code applicatif (BlogSpotService.Details) s'appuiera sur cette
contrainte dans un commit séparé.
This commit is contained in:
Lum 2026-07-11 18:45:27 +01:00
commit 98613e7070
5 changed files with 4793 additions and 3 deletions

View file

@ -214,6 +214,22 @@ namespace Yavsc.Models
// Log immuable — pas de update autorisé
e.ToTable(tb => tb.HasCheckConstraint("CK_ModerationLog_Immutable", "1=1"));
});
// ── Blog FK strictness ─────────────────────────────────────────────
// Tout billet a un auteur, tout commentaire a un auteur : pas de
// cascade en suppression d'un user, pas d'orphelin toléré. Le code
// applicatif (BlogSpotService.Details) s'appuie sur cette
// contrainte pour pouvoir assumer l'existence de l'auteur.
builder.Entity<BlogPost>()
.HasOne(b => b.Author)
.WithMany(u => u.Posts)
.HasForeignKey(b => b.AuthorId)
.OnDelete(DeleteBehavior.Restrict);
builder.Entity<Comment>()
.HasOne(c => c.Author)
.WithMany(u => u.BlogComments)
.HasForeignKey(c => c.AuthorId)
.OnDelete(DeleteBehavior.Restrict);
}
/// <summary>

View file

@ -114,6 +114,13 @@ namespace Yavsc.Models
[InverseProperty("Member")]
public virtual List<CircleMember>? Membership { get; set; }
/// <summary>
/// User's blog comments
/// </summary>
[JsonIgnore]
[InverseProperty("Author")]
public virtual List<Blog.Comment>? BlogComments { get; set; }
IAccountBalance? IApplicationUser.AccountBalance => AccountBalance;
ILocation? IApplicationUser.PostalAddress { get => PostalAddress; }