From 3024ed85d233b79c7964f4ea570a8626fc5cfbfa Mon Sep 17 00:00:00 2001 From: beo3000 Date: Sun, 21 Dec 2025 12:03:46 +0100 Subject: [PATCH] fix db model --- src/Koogle.Domain/Entities/PersonExpense.cs | 10 +++---- .../Data/AppDbContext.cs | 29 +++++++++++++++++++ .../Data/Configurations/ClubConfiguration.cs | 2 ++ .../Configurations/DayPersonConfiguration.cs | 8 ++--- .../Configurations/ExpenseConfiguration.cs | 3 +- .../ExpenseTriggerConfiguration.cs | 4 +-- .../Data/Configurations/GameConfiguration.cs | 2 +- .../Configurations/GamePersonConfiguration.cs | 6 ++-- .../Configurations/PersonConfiguration.cs | 2 +- .../PersonExpenseConfiguration.cs | 21 ++++++++++---- .../Configurations/TriggerConfiguration.cs | 2 ++ .../UserProfileClubConfiguration.cs | 4 +-- 12 files changed, 67 insertions(+), 26 deletions(-) diff --git a/src/Koogle.Domain/Entities/PersonExpense.cs b/src/Koogle.Domain/Entities/PersonExpense.cs index 6e9e38b..d7e19a1 100644 --- a/src/Koogle.Domain/Entities/PersonExpense.cs +++ b/src/Koogle.Domain/Entities/PersonExpense.cs @@ -30,6 +30,11 @@ public class PersonExpense : BaseEntity /// public Guid? GameId { get; set; } // Foreign Key + /// + /// Id of a club. + /// + public Guid ClubId { get; set; } + // Stammdaten /// @@ -57,11 +62,6 @@ public class PersonExpense : BaseEntity // Navigation Properties - /// - /// Id of a club. - /// - public Guid ClubId { get; set; } - /// /// ID des Benutzers, der die Zuordnung vorgenommen hat. /// diff --git a/src/Koogle.Infrastructure/Data/AppDbContext.cs b/src/Koogle.Infrastructure/Data/AppDbContext.cs index 936a76d..3dc06f1 100644 --- a/src/Koogle.Infrastructure/Data/AppDbContext.cs +++ b/src/Koogle.Infrastructure/Data/AppDbContext.cs @@ -42,4 +42,33 @@ public class AppDbContext : DbContext // Deine IEntityTypeConfiguration<> Klassen automatisch anwenden modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly); } + + + + public override int SaveChanges() + { + ConvertDeletesToSoftDeletes(); + return base.SaveChanges(); + } + + public override Task SaveChangesAsync(CancellationToken cancellationToken = default) + { + ConvertDeletesToSoftDeletes(); + return base.SaveChangesAsync(cancellationToken); + } + + private void ConvertDeletesToSoftDeletes() + { + foreach (var entry in ChangeTracker.Entries()) + { + if (entry.State == EntityState.Deleted) + { + entry.State = EntityState.Modified; + entry.Entity.IsDeleted = true; + entry.Entity.ModifiedAt = DateTime.UtcNow; + } + } + } + + } diff --git a/src/Koogle.Infrastructure/Data/Configurations/ClubConfiguration.cs b/src/Koogle.Infrastructure/Data/Configurations/ClubConfiguration.cs index 4f6a7fc..d08c2ae 100644 --- a/src/Koogle.Infrastructure/Data/Configurations/ClubConfiguration.cs +++ b/src/Koogle.Infrastructure/Data/Configurations/ClubConfiguration.cs @@ -21,6 +21,8 @@ public class ClubConfiguration : IEntityTypeConfiguration .HasConversion() .IsRequired(); + builder.HasIndex(x => x.Name); + builder.HasQueryFilter(x => !x.IsDeleted); } } \ No newline at end of file diff --git a/src/Koogle.Infrastructure/Data/Configurations/DayPersonConfiguration.cs b/src/Koogle.Infrastructure/Data/Configurations/DayPersonConfiguration.cs index c3f3dfc..d92fa54 100644 --- a/src/Koogle.Infrastructure/Data/Configurations/DayPersonConfiguration.cs +++ b/src/Koogle.Infrastructure/Data/Configurations/DayPersonConfiguration.cs @@ -32,14 +32,14 @@ public class DayPersonConfiguration : IEntityTypeConfiguration .WithMany(x => x.DayPersons) .HasForeignKey(x => new { x.DayId, x.ClubId }) .HasPrincipalKey(d => new { d.Id, d.ClubId }) - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.NoAction); // Club-Konsistenz: (PersonId, ClubId) muss in Person(Id, ClubId) existieren builder.HasOne(x => x.Person) .WithMany(x => x.DayPersons) .HasForeignKey(x => new { x.PersonId, x.ClubId }) .HasPrincipalKey(p => new { p.Id, p.ClubId }) - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.NoAction); @@ -58,10 +58,6 @@ public class DayPersonConfiguration : IEntityTypeConfiguration - builder.HasIndex(x => x.ClubId); - builder.HasIndex(x => x.DayId); - builder.HasIndex(x => x.PersonId); - } } diff --git a/src/Koogle.Infrastructure/Data/Configurations/ExpenseConfiguration.cs b/src/Koogle.Infrastructure/Data/Configurations/ExpenseConfiguration.cs index b60b2ec..d80c365 100644 --- a/src/Koogle.Infrastructure/Data/Configurations/ExpenseConfiguration.cs +++ b/src/Koogle.Infrastructure/Data/Configurations/ExpenseConfiguration.cs @@ -42,12 +42,13 @@ public class ExpenseConfiguration : IEntityTypeConfiguration builder.HasOne(x => x.Club) .WithMany() .HasForeignKey(x => x.ClubId) - .OnDelete(DeleteBehavior.Restrict); + .OnDelete(DeleteBehavior.NoAction); // Alternate Key für Composite-FK in ExpenseTrigger: (Id, ClubId) builder.HasAlternateKey(x => new { x.Id, x.ClubId }); builder.HasIndex(x => x.ClubId); + builder.HasIndex(x => new { x.ClubId, x.Name }); builder.HasQueryFilter(x => !x.IsDeleted); diff --git a/src/Koogle.Infrastructure/Data/Configurations/ExpenseTriggerConfiguration.cs b/src/Koogle.Infrastructure/Data/Configurations/ExpenseTriggerConfiguration.cs index 06475d9..e7bbf32 100644 --- a/src/Koogle.Infrastructure/Data/Configurations/ExpenseTriggerConfiguration.cs +++ b/src/Koogle.Infrastructure/Data/Configurations/ExpenseTriggerConfiguration.cs @@ -38,12 +38,12 @@ public class ExpenseTriggerConfiguration : IEntityTypeConfiguration new { x.ExpenseId, x.ClubId }) .HasPrincipalKey(e => new { e.Id, e.ClubId }) - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.NoAction); builder.HasOne(x => x.Trigger) .WithMany() .HasForeignKey(x => x.TriggerId) - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.NoAction); builder.HasIndex(x => x.ExpenseId); builder.HasIndex(x => x.TriggerId); diff --git a/src/Koogle.Infrastructure/Data/Configurations/GameConfiguration.cs b/src/Koogle.Infrastructure/Data/Configurations/GameConfiguration.cs index 57f2850..49656a5 100644 --- a/src/Koogle.Infrastructure/Data/Configurations/GameConfiguration.cs +++ b/src/Koogle.Infrastructure/Data/Configurations/GameConfiguration.cs @@ -39,7 +39,7 @@ public class GameConfiguration : IEntityTypeConfiguration builder.HasOne(x => x.Day) .WithMany() .HasForeignKey(x => x.DayId) - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.NoAction); // Mehrere Games pro Day -> Index nicht unique builder.HasIndex(x => x.DayId); diff --git a/src/Koogle.Infrastructure/Data/Configurations/GamePersonConfiguration.cs b/src/Koogle.Infrastructure/Data/Configurations/GamePersonConfiguration.cs index efc0161..834ca64 100644 --- a/src/Koogle.Infrastructure/Data/Configurations/GamePersonConfiguration.cs +++ b/src/Koogle.Infrastructure/Data/Configurations/GamePersonConfiguration.cs @@ -20,14 +20,16 @@ public class GamePersonConfiguration : IEntityTypeConfiguration builder.HasOne(x => x.Game) .WithMany(x => x.GamePersons) .HasForeignKey(x => x.GameId) - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.NoAction); builder.HasOne(x => x.Person) .WithMany() .HasForeignKey(x => x.PersonId) - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.NoAction); + builder.HasIndex(x => x.PersonId); + builder.HasIndex(x => x.GameId); // ✅ Matching Query Filter: diff --git a/src/Koogle.Infrastructure/Data/Configurations/PersonConfiguration.cs b/src/Koogle.Infrastructure/Data/Configurations/PersonConfiguration.cs index 4bcaf3c..f583d9d 100644 --- a/src/Koogle.Infrastructure/Data/Configurations/PersonConfiguration.cs +++ b/src/Koogle.Infrastructure/Data/Configurations/PersonConfiguration.cs @@ -36,7 +36,7 @@ public class PersonConfiguration : IEntityTypeConfiguration builder.HasMany(x => x.Expenses) .WithOne(x => x.Person) .HasForeignKey(x => x.PersonId) - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.NoAction); builder.HasIndex(x => x.ClubId); diff --git a/src/Koogle.Infrastructure/Data/Configurations/PersonExpenseConfiguration.cs b/src/Koogle.Infrastructure/Data/Configurations/PersonExpenseConfiguration.cs index 20a6763..bc8b449 100644 --- a/src/Koogle.Infrastructure/Data/Configurations/PersonExpenseConfiguration.cs +++ b/src/Koogle.Infrastructure/Data/Configurations/PersonExpenseConfiguration.cs @@ -39,40 +39,49 @@ public class PersonExpenseConfiguration : IEntityTypeConfiguration x.AssignedById).IsRequired(); + // Soft-delete only => DB soll nicht cascade-löschen builder.HasOne(x => x.Person) .WithMany(x => x.Expenses) .HasForeignKey(x => x.PersonId) - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.NoAction); builder.HasOne(x => x.Day) .WithMany() .HasForeignKey(x => x.DayId) - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.NoAction); builder.HasOne(x => x.Expense) .WithMany() .HasForeignKey(x => x.ExpenseId) - .OnDelete(DeleteBehavior.Restrict); + .OnDelete(DeleteBehavior.NoAction); + builder.HasOne(x => x.Game) .WithMany() .HasForeignKey(x => x.GameId) - .OnDelete(DeleteBehavior.SetNull); + // SQL Server: verhindert multiple cascade paths (SET NULL ist eine kaskadierende Aktion) + .OnDelete(DeleteBehavior.NoAction); + + + builder.HasIndex(x => x.ClubId); builder.HasIndex(x => new { x.PersonId, x.DayId }); builder.HasIndex(x => x.DayId); builder.HasIndex(x => x.GameId); + + builder.HasQueryFilter(pe => - // required Principals müssen sichtbar sein + !pe.IsDeleted && + !pe.Club.IsDeleted && !pe.Person.IsDeleted && !pe.Day.IsDeleted && !pe.Expense.IsDeleted && - // optionales Game: entweder kein Game, oder Game + dessen Day sichtbar (pe.GameId == null || (!pe.Game!.IsDeleted && !pe.Game!.Day.IsDeleted)) ); + } } diff --git a/src/Koogle.Infrastructure/Data/Configurations/TriggerConfiguration.cs b/src/Koogle.Infrastructure/Data/Configurations/TriggerConfiguration.cs index 5c314cf..c6cce4a 100644 --- a/src/Koogle.Infrastructure/Data/Configurations/TriggerConfiguration.cs +++ b/src/Koogle.Infrastructure/Data/Configurations/TriggerConfiguration.cs @@ -26,6 +26,8 @@ public class TriggerConfiguration : IEntityTypeConfiguration .HasConversion() .IsRequired(); + builder.HasIndex(x => x.ExpenseTriggerType); + builder.HasQueryFilter(x => !x.IsDeleted); } } diff --git a/src/Koogle.Infrastructure/Data/Configurations/UserProfileClubConfiguration.cs b/src/Koogle.Infrastructure/Data/Configurations/UserProfileClubConfiguration.cs index 9dd60d7..ff838e4 100644 --- a/src/Koogle.Infrastructure/Data/Configurations/UserProfileClubConfiguration.cs +++ b/src/Koogle.Infrastructure/Data/Configurations/UserProfileClubConfiguration.cs @@ -20,12 +20,12 @@ public class UserProfileClubConfiguration : IEntityTypeConfiguration x.UserProfile) .WithMany(x => x.Clubs) .HasForeignKey(x => x.UserProfileId) - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.NoAction); builder.HasOne(x => x.Club) .WithMany() .HasForeignKey(x => x.ClubId) - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.NoAction); builder.Property(x => x.IsDefault).IsRequired();