From 8669a0124cbf7a05794992ec9616690a39dbbf86 Mon Sep 17 00:00:00 2001 From: beo3000 Date: Sat, 20 Dec 2025 21:31:32 +0100 Subject: [PATCH] fix data model --- .../Data/Configurations/DayConfiguration.cs | 19 +++++----- .../Configurations/ExpenseConfiguration.cs | 7 +++- .../ExpenseTriggerConfiguration.cs | 16 +++++++-- .../Configurations/PersonConfiguration.cs | 19 +++++----- .../UserProfileClubConfiguration.cs | 36 +++++++++++-------- Koogle.Infrastrcuture/DependencyInjection.cs | 2 +- 6 files changed, 60 insertions(+), 39 deletions(-) diff --git a/Koogle.Infrastrcuture/Data/Configurations/DayConfiguration.cs b/Koogle.Infrastrcuture/Data/Configurations/DayConfiguration.cs index 2098c77..44c1fdb 100644 --- a/Koogle.Infrastrcuture/Data/Configurations/DayConfiguration.cs +++ b/Koogle.Infrastrcuture/Data/Configurations/DayConfiguration.cs @@ -14,6 +14,7 @@ public class DayConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { + builder.ToTable("Days"); builder.HasKey(x => x.Id); @@ -25,26 +26,24 @@ public class DayConfiguration : IEntityTypeConfiguration .HasConversion() .IsRequired(); - builder.Property(x => x.ClubId).IsRequired(); + builder.Property(x => x.ClubId) + .IsRequired(); builder.HasOne(x => x.Club) .WithMany() .HasForeignKey(x => x.ClubId) .OnDelete(DeleteBehavior.Restrict); - - // DayConfiguration: Relation Day -> DayPerson über (DayId, ClubId) - builder.HasMany(d => d.DayPersons) - .WithOne(dp => dp.Day) - .HasForeignKey(dp => new { dp.DayId, dp.ClubId }) - .HasPrincipalKey(d => new { d.Id, d.ClubId }); - - - // Wichtig für DayPerson-Composite FK: Alternate Key (Id, ClubId) + // Alternate Key für DayPerson-Composite FK: (Id, ClubId) builder.HasAlternateKey(x => new { x.Id, x.ClubId }); builder.HasIndex(x => new { x.ClubId, x.PostDate }); builder.HasQueryFilter(x => !x.IsDeleted); + + // Wichtig: + // Keine HasMany(x => x.DayPersons) hier definieren, weil die Club-Konsistenz + // via Composite-FK in DayPersonConfiguration abgebildet wird. + } } diff --git a/Koogle.Infrastrcuture/Data/Configurations/ExpenseConfiguration.cs b/Koogle.Infrastrcuture/Data/Configurations/ExpenseConfiguration.cs index e32dc21..b60b2ec 100644 --- a/Koogle.Infrastrcuture/Data/Configurations/ExpenseConfiguration.cs +++ b/Koogle.Infrastrcuture/Data/Configurations/ExpenseConfiguration.cs @@ -17,7 +17,8 @@ public class ExpenseConfiguration : IEntityTypeConfiguration builder.HasKey(x => x.Id); - builder.Property(x => x.ClubId).IsRequired(); + builder.Property(x => x.ClubId) + .IsRequired(); builder.Property(x => x.Name) .HasMaxLength(200) @@ -43,9 +44,13 @@ public class ExpenseConfiguration : IEntityTypeConfiguration .HasForeignKey(x => x.ClubId) .OnDelete(DeleteBehavior.Restrict); + // Alternate Key für Composite-FK in ExpenseTrigger: (Id, ClubId) + builder.HasAlternateKey(x => new { x.Id, x.ClubId }); + builder.HasIndex(x => x.ClubId); builder.HasQueryFilter(x => !x.IsDeleted); + } } diff --git a/Koogle.Infrastrcuture/Data/Configurations/ExpenseTriggerConfiguration.cs b/Koogle.Infrastrcuture/Data/Configurations/ExpenseTriggerConfiguration.cs index 37fe2d4..75c69c1 100644 --- a/Koogle.Infrastrcuture/Data/Configurations/ExpenseTriggerConfiguration.cs +++ b/Koogle.Infrastrcuture/Data/Configurations/ExpenseTriggerConfiguration.cs @@ -14,22 +14,30 @@ public class ExpenseTriggerConfiguration : IEntityTypeConfiguration builder) { + builder.ToTable("ExpenseTriggers"); // Eine aktive Zuordnung pro Club/Expense/Trigger builder.HasKey(x => new { x.ClubId, x.ExpenseId, x.TriggerId }); - builder.Property(x => x.AssignedAt).IsRequired(); - builder.Property(x => x.AssignedById).IsRequired(); + builder.Property(x => x.AssignedAt) + .IsRequired() + .HasDefaultValueSql("SYSUTCDATETIME()"); + + builder.Property(x => x.AssignedById) + .IsRequired(); builder.HasOne(x => x.Club) .WithMany() .HasForeignKey(x => x.ClubId) .OnDelete(DeleteBehavior.Cascade); + // WICHTIG: Club-Konsistenz erzwingen: + // (ExpenseId, ClubId) muss in Expense (Id, ClubId) existieren builder.HasOne(x => x.Expense) .WithMany() - .HasForeignKey(x => x.ExpenseId) + .HasForeignKey(x => new { x.ExpenseId, x.ClubId }) + .HasPrincipalKey(e => new { e.Id, e.ClubId }) .OnDelete(DeleteBehavior.Cascade); builder.HasOne(x => x.Trigger) @@ -39,6 +47,8 @@ public class ExpenseTriggerConfiguration : IEntityTypeConfiguration x.ExpenseId); builder.HasIndex(x => x.TriggerId); + builder.HasIndex(x => x.ClubId); + } } diff --git a/Koogle.Infrastrcuture/Data/Configurations/PersonConfiguration.cs b/Koogle.Infrastrcuture/Data/Configurations/PersonConfiguration.cs index 92879df..4bcaf3c 100644 --- a/Koogle.Infrastrcuture/Data/Configurations/PersonConfiguration.cs +++ b/Koogle.Infrastrcuture/Data/Configurations/PersonConfiguration.cs @@ -9,6 +9,7 @@ public class PersonConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { + builder.ToTable("Persons"); builder.HasKey(x => x.Id); @@ -21,14 +22,15 @@ public class PersonConfiguration : IEntityTypeConfiguration .HasConversion() .IsRequired(); - builder.Property(x => x.ClubId).IsRequired(); + builder.Property(x => x.ClubId) + .IsRequired(); builder.HasOne(x => x.Club) .WithMany() .HasForeignKey(x => x.ClubId) .OnDelete(DeleteBehavior.Restrict); - // Wichtig für DayPerson-Composite FK: Alternate Key (Id, ClubId) + // Alternate Key für DayPerson-Composite FK: (Id, ClubId) builder.HasAlternateKey(x => new { x.Id, x.ClubId }); builder.HasMany(x => x.Expenses) @@ -36,16 +38,13 @@ public class PersonConfiguration : IEntityTypeConfiguration .HasForeignKey(x => x.PersonId) .OnDelete(DeleteBehavior.Cascade); - - // PersonConfiguration: Relation Person -> DayPerson über (PersonId, ClubId) - builder.HasMany(p => p.DayPersons) - .WithOne(dp => dp.Person) - .HasForeignKey(dp => new { dp.PersonId, dp.ClubId }) - .HasPrincipalKey(p => new { p.Id, p.ClubId }); - - builder.HasIndex(x => x.ClubId); builder.HasQueryFilter(x => !x.IsDeleted); + + // Wichtig: + // Keine HasMany(x => x.DayPersons) hier definieren, weil die Club-Konsistenz + // via Composite-FK in DayPersonConfiguration abgebildet wird. + } } diff --git a/Koogle.Infrastrcuture/Data/Configurations/UserProfileClubConfiguration.cs b/Koogle.Infrastrcuture/Data/Configurations/UserProfileClubConfiguration.cs index 7ee37ea..3b9cc7b 100644 --- a/Koogle.Infrastrcuture/Data/Configurations/UserProfileClubConfiguration.cs +++ b/Koogle.Infrastrcuture/Data/Configurations/UserProfileClubConfiguration.cs @@ -13,32 +13,40 @@ public class UserProfileClubConfiguration : IEntityTypeConfiguration builder) { + builder.ToTable("UserProfileClubs"); + // 1 Zuordnung pro (UserProfile, Club) builder.HasKey(x => new { x.UserProfileId, x.ClubId }); - - builder.Property(x => x.AssignedAt).IsRequired(); - builder.Property(x => x.AssignedById).IsRequired(); - // optional default - builder.Property(x => x.AssignedAt).HasDefaultValueSql("SYSUTCDATETIME()"); - - - builder.HasOne(x => x.Club) - .WithMany() // oder: Club.UserProfiles wenn du das als Navigation ergänzt - .HasForeignKey(x => x.ClubId) - .OnDelete(DeleteBehavior.Cascade); - - + // Beziehung zu UserProfile (fehlte) builder.HasOne(x => x.UserProfile) .WithMany(x => x.Clubs) .HasForeignKey(x => x.UserProfileId) .OnDelete(DeleteBehavior.Cascade); + // Beziehung zu Club + builder.HasOne(x => x.Club) + .WithMany() + .HasForeignKey(x => x.ClubId) + .OnDelete(DeleteBehavior.Cascade); - // Optional: nur einen Default-Club je Profil erzwingen (SQL Server: filtered index) + builder.Property(x => x.IsDefault) + .IsRequired(); + + builder.Property(x => x.AssignedAt) + .IsRequired() + .HasDefaultValueSql("SYSUTCDATETIME()"); + + builder.Property(x => x.AssignedById) + .IsRequired(); + + // SQL Server: pro UserProfile nur EIN Default-Club builder.HasIndex(x => x.UserProfileId) .HasFilter("[IsDefault] = 1") .IsUnique(); + + builder.HasIndex(x => x.ClubId); + } } diff --git a/Koogle.Infrastrcuture/DependencyInjection.cs b/Koogle.Infrastrcuture/DependencyInjection.cs index 9aa665c..2cd03bf 100644 --- a/Koogle.Infrastrcuture/DependencyInjection.cs +++ b/Koogle.Infrastrcuture/DependencyInjection.cs @@ -13,7 +13,7 @@ using System.Reflection; using System.Text; using System.Threading.Tasks; -namespace Koogle.Infrastrcuture +namespace Koogle.Infrastructure { public static class DependencyInjection {