From 7457e77867bce11c7812ed4f5ac8ac27b89b6a13 Mon Sep 17 00:00:00 2001 From: Simon Diesenreiter Date: Sun, 20 Jul 2025 16:46:34 +0200 Subject: [PATCH] fix: better data model for liked songs, refs NOISSUE --- song_of_the_day/Data/DataContext.cs | 13 + ...model liked songs relationship.Designer.cs | 296 ++++++++++++++++++ ...plicitly model liked songs relationship.cs | 80 +++++ .../Migrations/DataContextModelSnapshot.cs | 44 ++- song_of_the_day/Data/Song.cs | 2 + 5 files changed, 421 insertions(+), 14 deletions(-) create mode 100644 song_of_the_day/Data/Migrations/20250720144512_explicitly model liked songs relationship.Designer.cs create mode 100644 song_of_the_day/Data/Migrations/20250720144512_explicitly model liked songs relationship.cs diff --git a/song_of_the_day/Data/DataContext.cs b/song_of_the_day/Data/DataContext.cs index 0778fd6..99683d1 100644 --- a/song_of_the_day/Data/DataContext.cs +++ b/song_of_the_day/Data/DataContext.cs @@ -18,4 +18,17 @@ public class DataContext : DbContext => optionsBuilder.UseNpgsql($"Host={AppConfiguration.Instance.DatabaseUri}:{AppConfiguration.Instance.DatabasePort};" + $"Username={AppConfiguration.Instance.DatabaseUser};Password={AppConfiguration.Instance.DatabasePW};" + $"Database={AppConfiguration.Instance.DatabaseName}"); + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + // configures one-to-many relationship + modelBuilder.Entity() + .HasMany(u => u.LikedSongs) + .WithMany(s => s.LikedBy) + .UsingEntity( + "LikedSongs", + r => r.HasOne(typeof(Song)).WithMany().HasForeignKey("SongId").HasPrincipalKey(nameof(Song.SongId)), + l => l.HasOne(typeof(User)).WithMany().HasForeignKey("UserId").HasPrincipalKey(nameof(User.UserId)), + j => j.HasKey("SongId", "UserId")); + } } \ No newline at end of file diff --git a/song_of_the_day/Data/Migrations/20250720144512_explicitly model liked songs relationship.Designer.cs b/song_of_the_day/Data/Migrations/20250720144512_explicitly model liked songs relationship.Designer.cs new file mode 100644 index 0000000..f785088 --- /dev/null +++ b/song_of_the_day/Data/Migrations/20250720144512_explicitly model liked songs relationship.Designer.cs @@ -0,0 +1,296 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace song_of_the_day.DataMigrations +{ + [DbContext(typeof(DataContext))] + [Migration("20250720144512_explicitly model liked songs relationship")] + partial class explicitlymodellikedsongsrelationship + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("LikedSongs", b => + { + b.Property("SongId") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("SongId", "UserId"); + + b.HasIndex("UserId"); + + b.ToTable("LikedSongs"); + }); + + modelBuilder.Entity("SmartPlaylistDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedByUserId") + .HasColumnType("integer"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("IncludesLikedSongs") + .HasColumnType("boolean"); + + b.Property("IncludesUnCategorizedSongs") + .HasColumnType("boolean"); + + b.Property("SpotifyPlaylistId") + .HasColumnType("text"); + + b.Property("Title") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CreatedByUserId"); + + b.ToTable("SmartPlaylistDefinitions"); + }); + + modelBuilder.Entity("Song", b => + { + b.Property("SongId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("SongId")); + + b.Property("Artist") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Provider") + .HasColumnType("integer"); + + b.Property("SmartPlaylistDefinitionId") + .HasColumnType("integer"); + + b.Property("SmartPlaylistDefinitionId1") + .HasColumnType("integer"); + + b.Property("SpotifyId") + .HasColumnType("text"); + + b.Property("Url") + .HasColumnType("text"); + + b.HasKey("SongId"); + + b.HasIndex("SmartPlaylistDefinitionId"); + + b.HasIndex("SmartPlaylistDefinitionId1"); + + b.ToTable("Songs"); + }); + + modelBuilder.Entity("SongSuggestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("HasUsedSuggestion") + .HasColumnType("boolean"); + + b.Property("SongId") + .HasColumnType("integer"); + + b.Property("SuggestionHelperId") + .HasColumnType("integer"); + + b.Property("UserHasSubmitted") + .HasColumnType("boolean"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("SongId"); + + b.HasIndex("SuggestionHelperId"); + + b.HasIndex("UserId"); + + b.ToTable("SongSuggestions"); + }); + + modelBuilder.Entity("SuggestionHelper", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("SmartPlaylistDefinitionId") + .HasColumnType("integer"); + + b.Property("Title") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("SmartPlaylistDefinitionId"); + + b.ToTable("SuggestionHelpers"); + }); + + modelBuilder.Entity("User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("UserId")); + + b.Property("AssociationInProgress") + .HasColumnType("boolean"); + + b.Property("IsIntroduced") + .HasColumnType("boolean"); + + b.Property("LdapUserName") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("NickName") + .HasColumnType("text"); + + b.Property("SignalMemberId") + .HasColumnType("text"); + + b.Property("SpotifyAuthAccessToken") + .HasColumnType("text"); + + b.Property("SpotifyAuthCreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SpotifyAuthExpiresAfterSeconds") + .HasColumnType("integer"); + + b.Property("SpotifyAuthRefreshToken") + .HasColumnType("text"); + + b.Property("WasChosenForSuggestionThisRound") + .HasColumnType("boolean"); + + b.HasKey("UserId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("LikedSongs", b => + { + b.HasOne("Song", null) + .WithMany() + .HasForeignKey("SongId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SmartPlaylistDefinition", b => + { + b.HasOne("User", "CreatedBy") + .WithMany() + .HasForeignKey("CreatedByUserId"); + + b.Navigation("CreatedBy"); + }); + + modelBuilder.Entity("Song", b => + { + b.HasOne("SmartPlaylistDefinition", null) + .WithMany("ExplicitlyExcludedSongs") + .HasForeignKey("SmartPlaylistDefinitionId"); + + b.HasOne("SmartPlaylistDefinition", null) + .WithMany("ExplicitlyIncludedSongs") + .HasForeignKey("SmartPlaylistDefinitionId1"); + }); + + modelBuilder.Entity("SongSuggestion", b => + { + b.HasOne("Song", "Song") + .WithMany() + .HasForeignKey("SongId"); + + b.HasOne("SuggestionHelper", "SuggestionHelper") + .WithMany() + .HasForeignKey("SuggestionHelperId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Song"); + + b.Navigation("SuggestionHelper"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("SuggestionHelper", b => + { + b.HasOne("SmartPlaylistDefinition", null) + .WithMany("Categories") + .HasForeignKey("SmartPlaylistDefinitionId"); + }); + + modelBuilder.Entity("SmartPlaylistDefinition", b => + { + b.Navigation("Categories"); + + b.Navigation("ExplicitlyExcludedSongs"); + + b.Navigation("ExplicitlyIncludedSongs"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/song_of_the_day/Data/Migrations/20250720144512_explicitly model liked songs relationship.cs b/song_of_the_day/Data/Migrations/20250720144512_explicitly model liked songs relationship.cs new file mode 100644 index 0000000..f30e3de --- /dev/null +++ b/song_of_the_day/Data/Migrations/20250720144512_explicitly model liked songs relationship.cs @@ -0,0 +1,80 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace song_of_the_day.DataMigrations +{ + /// + public partial class explicitlymodellikedsongsrelationship : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Songs_Users_UserId", + table: "Songs"); + + migrationBuilder.DropIndex( + name: "IX_Songs_UserId", + table: "Songs"); + + migrationBuilder.DropColumn( + name: "UserId", + table: "Songs"); + + migrationBuilder.CreateTable( + name: "LikedSongs", + columns: table => new + { + SongId = table.Column(type: "integer", nullable: false), + UserId = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_LikedSongs", x => new { x.SongId, x.UserId }); + table.ForeignKey( + name: "FK_LikedSongs_Songs_SongId", + column: x => x.SongId, + principalTable: "Songs", + principalColumn: "SongId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_LikedSongs_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "UserId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_LikedSongs_UserId", + table: "LikedSongs", + column: "UserId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "LikedSongs"); + + migrationBuilder.AddColumn( + name: "UserId", + table: "Songs", + type: "integer", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_Songs_UserId", + table: "Songs", + column: "UserId"); + + migrationBuilder.AddForeignKey( + name: "FK_Songs_Users_UserId", + table: "Songs", + column: "UserId", + principalTable: "Users", + principalColumn: "UserId"); + } + } +} diff --git a/song_of_the_day/Data/Migrations/DataContextModelSnapshot.cs b/song_of_the_day/Data/Migrations/DataContextModelSnapshot.cs index 7c951ed..0edb325 100644 --- a/song_of_the_day/Data/Migrations/DataContextModelSnapshot.cs +++ b/song_of_the_day/Data/Migrations/DataContextModelSnapshot.cs @@ -21,6 +21,21 @@ namespace song_of_the_day.DataMigrations NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.Entity("LikedSongs", b => + { + b.Property("SongId") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("SongId", "UserId"); + + b.HasIndex("UserId"); + + b.ToTable("LikedSongs"); + }); + modelBuilder.Entity("SmartPlaylistDefinition", b => { b.Property("Id") @@ -83,17 +98,12 @@ namespace song_of_the_day.DataMigrations b.Property("Url") .HasColumnType("text"); - b.Property("UserId") - .HasColumnType("integer"); - b.HasKey("SongId"); b.HasIndex("SmartPlaylistDefinitionId"); b.HasIndex("SmartPlaylistDefinitionId1"); - b.HasIndex("UserId"); - b.ToTable("Songs"); }); @@ -204,6 +214,21 @@ namespace song_of_the_day.DataMigrations b.ToTable("Users"); }); + modelBuilder.Entity("LikedSongs", b => + { + b.HasOne("Song", null) + .WithMany() + .HasForeignKey("SongId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + modelBuilder.Entity("SmartPlaylistDefinition", b => { b.HasOne("User", "CreatedBy") @@ -222,10 +247,6 @@ namespace song_of_the_day.DataMigrations b.HasOne("SmartPlaylistDefinition", null) .WithMany("ExplicitlyIncludedSongs") .HasForeignKey("SmartPlaylistDefinitionId1"); - - b.HasOne("User", null) - .WithMany("LikedSongs") - .HasForeignKey("UserId"); }); modelBuilder.Entity("SongSuggestion", b => @@ -266,11 +287,6 @@ namespace song_of_the_day.DataMigrations b.Navigation("ExplicitlyIncludedSongs"); }); - - modelBuilder.Entity("User", b => - { - b.Navigation("LikedSongs"); - }); #pragma warning restore 612, 618 } } diff --git a/song_of_the_day/Data/Song.cs b/song_of_the_day/Data/Song.cs index e902e04..ba6f31b 100644 --- a/song_of_the_day/Data/Song.cs +++ b/song_of_the_day/Data/Song.cs @@ -8,4 +8,6 @@ public class Song public string? Url { get; set; } public SongProvider? Provider { get; set; } public string? SpotifyId { get; set; } + + public IList LikedBy { get; set; } } \ No newline at end of file