From ea73d03b8f2a340f96f6ca3b944e05f9b154096e Mon Sep 17 00:00:00 2001 From: glax Date: Mon, 30 Jun 2025 14:42:24 +0200 Subject: [PATCH] WIP: Manga can be linked to multiple Connectors - PgsqlContext Adjustment --- API/Schema/Chapter.cs | 16 +++++++- API/Schema/Contexts/PgsqlContext.cs | 57 +++++++++++++++----------- API/Schema/Jobs/RetrieveChaptersJob.cs | 2 +- API/Schema/Manga.cs | 5 +-- 4 files changed, 49 insertions(+), 31 deletions(-) diff --git a/API/Schema/Chapter.cs b/API/Schema/Chapter.cs index 408d83f..71ce9fe 100644 --- a/API/Schema/Chapter.cs +++ b/API/Schema/Chapter.cs @@ -15,7 +15,21 @@ public class Chapter : IComparable [StringLength(64)] [Required] public string ChapterId { get; init; } [StringLength(256)]public string? IdOnConnectorSite { get; init; } - + + [StringLength(64)] [Required] public string ParentMangaId { get; init; } = null!; + private Manga? _parentManga = null!; + + [JsonIgnore] + public Manga ParentManga + { + get => _lazyLoader.Load(this, ref _parentManga) ?? throw new InvalidOperationException(); + init + { + ParentMangaId = value.MangaId; + _parentManga = value; + } + } + private MangaConnectorMangaEntry? _mangaConnectorMangaEntry = null!; [JsonIgnore] public MangaConnectorMangaEntry MangaConnectorMangaEntry diff --git a/API/Schema/Contexts/PgsqlContext.cs b/API/Schema/Contexts/PgsqlContext.cs index 679e036..6b73821 100644 --- a/API/Schema/Contexts/PgsqlContext.cs +++ b/API/Schema/Contexts/PgsqlContext.cs @@ -40,31 +40,40 @@ public class PgsqlContext(DbContextOptions options) : DbContext(op .HasValue(JobType.RetrieveChaptersJob) .HasValue(JobType.UpdateCoverJob) .HasValue(JobType.UpdateChaptersDownloadedJob); + modelBuilder.Entity() + .HasDiscriminator(j => j.GetType().IsSubclassOf(typeof(JobWithDownloading))) + .HasValue(true); //Job specification - modelBuilder.Entity() - .HasOne(j => j.Manga) + modelBuilder.Entity() + .HasOne(j => j.MangaConnector) + .WithMany() + .HasForeignKey(j => j.MangaConnectorName) + .OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity() + .Navigation(j => j.MangaConnector) + .EnableLazyLoading(); + + modelBuilder.Entity() + .HasOne(j => j.MangaConnectorMangaEntry) .WithMany() - .HasForeignKey(j => j.MangaId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() - .Navigation(j => j.Manga) + .Navigation(j => j.MangaConnectorMangaEntry) .EnableLazyLoading(); modelBuilder.Entity() - .HasOne(j => j.Manga) + .HasOne(j => j.MangaConnectorMangaEntry) .WithMany() - .HasForeignKey(j => j.MangaId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() - .Navigation(j => j.Manga) + .Navigation(j => j.MangaConnectorMangaEntry) .EnableLazyLoading(); modelBuilder.Entity() - .HasOne(j => j.Chapter) + .HasOne(j => j.MangaConnectorMangaEntry) .WithMany() - .HasForeignKey(j => j.ChapterId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() - .Navigation(j => j.Chapter) + .Navigation(j => j.MangaConnectorMangaEntry) .EnableLazyLoading(); modelBuilder.Entity() .HasOne(j => j.Manga) @@ -83,12 +92,11 @@ public class PgsqlContext(DbContextOptions options) : DbContext(op .Navigation(j => j.ToLibrary) .EnableLazyLoading(); modelBuilder.Entity() - .HasOne(j => j.Manga) + .HasOne(j => j.MangaConnectorMangaEntry) .WithMany() - .HasForeignKey(j => j.MangaId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() - .Navigation(j => j.Manga) + .Navigation(j => j.MangaConnectorMangaEntry) .EnableLazyLoading(); modelBuilder.Entity() .HasOne(j => j.Manga) @@ -120,15 +128,6 @@ public class PgsqlContext(DbContextOptions options) : DbContext(op .HasValue("Global") .HasValue("MangaDex") .HasValue("ComickIo"); - //MangaConnector is responsible for many Manga - modelBuilder.Entity() - .HasMany() - .WithOne(m => m.MangaConnector) - .HasForeignKey(m => m.MangaConnectorName) - .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity() - .Navigation(m => m.MangaConnector) - .AutoInclude(); //Manga has many Chapters modelBuilder.Entity() @@ -136,9 +135,6 @@ public class PgsqlContext(DbContextOptions options) : DbContext(op .WithOne(c => c.ParentManga) .HasForeignKey(c => c.ParentMangaId) .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity() - .Navigation(c => c.ParentManga) - .AutoInclude(); modelBuilder.Entity() .Navigation(m => m.Chapters) .AutoInclude(false) @@ -181,6 +177,17 @@ public class PgsqlContext(DbContextOptions options) : DbContext(op modelBuilder.Entity() .Navigation(m => m.Authors) .AutoInclude(); + //Manga has many MangaConnectorMangaEntries with one MangaConnector + modelBuilder.Entity() + .HasMany(m => m.MangaConnectorLinkedToManga) + .WithOne(e => e.Manga) + .HasForeignKey(e => e.MangaId) + .OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity() + .HasOne(e => e.MangaConnector) + .WithMany() + .HasForeignKey(e => e.MangaConnectorName) + .OnDelete(DeleteBehavior.Cascade); //LocalLibrary has many Mangas modelBuilder.Entity() diff --git a/API/Schema/Jobs/RetrieveChaptersJob.cs b/API/Schema/Jobs/RetrieveChaptersJob.cs index 42b8616..6a9fccb 100644 --- a/API/Schema/Jobs/RetrieveChaptersJob.cs +++ b/API/Schema/Jobs/RetrieveChaptersJob.cs @@ -37,7 +37,7 @@ public class RetrieveChaptersJob : JobWithDownloading protected override IEnumerable RunInternal(PgsqlContext context) { // This gets all chapters that are not downloaded - Chapter[] allChapters = MangaConnectorMangaEntry.MangaConnector.GetChapters(MangaConnectorMangaEntry.Manga, Language).DistinctBy(c => c.ChapterId).ToArray(); + Chapter[] allChapters = MangaConnectorMangaEntry.MangaConnector.GetChapters(MangaConnectorMangaEntry, Language).DistinctBy(c => c.ChapterId).ToArray(); Chapter[] newChapters = allChapters.Where(chapter => MangaConnectorMangaEntry.Manga.Chapters.Select(c => c.ChapterId).Contains(chapter.ChapterId) == false).ToArray(); Log.Info($"{MangaConnectorMangaEntry.Manga.Chapters.Count} existing + {newChapters.Length} new chapters."); diff --git a/API/Schema/Manga.cs b/API/Schema/Manga.cs index 5173614..9fd80b4 100644 --- a/API/Schema/Manga.cs +++ b/API/Schema/Manga.cs @@ -2,7 +2,6 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Runtime.InteropServices; using System.Text; -using API.Schema.MangaConnectors; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Newtonsoft.Json; @@ -13,9 +12,7 @@ namespace API.Schema; [PrimaryKey("MangaId")] public class Manga { - [StringLength(64)] - [Required] - public string MangaId { get; init; } + [StringLength(64)] [Required] public string MangaId { get; init; } [StringLength(512)] [Required] public string Name { get; internal set; } [Required] public string Description { get; internal set; } [JsonIgnore] [Url] [StringLength(512)] public string CoverUrl { get; internal set; }