From f2bc48bc52bdadaf5dbfd09bbb4ceca11701ed1b Mon Sep 17 00:00:00 2001 From: glax Date: Mon, 21 Jul 2025 12:24:54 +0200 Subject: [PATCH] Context Load Navigations and Collections --- API/Controllers/MangaController.cs | 61 +++++++++++++++++-- API/Schema/MangaContext/Chapter.cs | 4 +- API/Schema/MangaContext/Manga.cs | 6 +- .../MetadataFetchers/MyAnimeList.cs | 6 ++ ...DownloadChapterFromMangaconnectorWorker.cs | 9 +++ .../DownloadCoverFromMangaconnectorWorker.cs | 2 + ...veMangaChaptersFromMangaconnectorWorker.cs | 4 ++ API/Workers/MoveMangaLibraryWorker.cs | 4 ++ .../CheckForNewChaptersWorker.cs | 5 +- .../UpdateChaptersDownloadedWorker.cs | 4 +- 10 files changed, 91 insertions(+), 14 deletions(-) diff --git a/API/Controllers/MangaController.cs b/API/Controllers/MangaController.cs index 9722da6..3150039 100644 --- a/API/Controllers/MangaController.cs +++ b/API/Controllers/MangaController.cs @@ -3,6 +3,8 @@ using API.Schema.MangaContext; using API.Workers; using Asp.Versioning; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.Net.Http.Headers; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Jpeg; @@ -21,12 +23,12 @@ public class MangaController(MangaContext context) : Controller /// /// Returns all cached /// - /// + /// Keys/IDs [HttpGet] - [ProducesResponseType(Status200OK, "application/json")] + [ProducesResponseType(Status200OK, "application/json")] public IActionResult GetAllManga() { - Manga[] ret = context.Mangas.ToArray(); + string[] ret = context.Mangas.Select(m => m.Key).ToArray(); return Ok(ret); } @@ -39,7 +41,15 @@ public class MangaController(MangaContext context) : Controller [ProducesResponseType(Status200OK, "application/json")] public IActionResult GetManga([FromBody]string[] MangaIds) { - Manga[] ret = context.Mangas.Where(m => MangaIds.Contains(m.Key)).ToArray(); + Manga[] ret = context.Mangas.Where(m => MangaIds.Contains(m.Key)) + .Include(m => m.Library) + .Include(m => m.Authors) + .Include(m => m.MangaTags) + .Include(m => m.Links) + .Include(m => m.AltTitles) + .Include(m => m.Chapters) + .Include(m => m.MangaConnectorIds) + .ToArray(); return Ok(ret); } @@ -56,6 +66,9 @@ public class MangaController(MangaContext context) : Controller { if (context.Mangas.Find(MangaId) is not { } manga) return NotFound(nameof(MangaId)); + foreach (CollectionEntry collectionEntry in context.Entry(manga).Collections) + collectionEntry.Load(); + context.Entry(manga).Navigation(nameof(Manga.Library)).Load(); return Ok(manga); } @@ -100,6 +113,15 @@ public class MangaController(MangaContext context) : Controller if (context.Mangas.Find(MangaIdInto) is not { } into) return NotFound(nameof(MangaIdInto)); + + foreach (CollectionEntry collectionEntry in context.Entry(from).Collections) + collectionEntry.Load(); + context.Entry(from).Navigation(nameof(Manga.Library)).Load(); + + foreach (CollectionEntry collectionEntry in context.Entry(into).Collections) + collectionEntry.Load(); + context.Entry(into).Navigation(nameof(Manga.Library)).Load(); + BaseWorker[] newJobs = into.MergeFrom(from, context); Tranga.AddWorkers(newJobs); @@ -173,6 +195,8 @@ public class MangaController(MangaContext context) : Controller if (context.Mangas.Find(MangaId) is not { } manga) return NotFound(nameof(MangaId)); + context.Entry(manga).Collection(m => m.Chapters).Load(); + Chapter[] chapters = manga.Chapters.ToArray(); return Ok(chapters); } @@ -192,6 +216,8 @@ public class MangaController(MangaContext context) : Controller { if (context.Mangas.Find(MangaId) is not { } manga) return NotFound(nameof(MangaId)); + + context.Entry(manga).Collection(m => m.Chapters).Load(); List chapters = manga.Chapters.Where(c => c.Downloaded).ToList(); if (chapters.Count == 0) @@ -216,6 +242,8 @@ public class MangaController(MangaContext context) : Controller if (context.Mangas.Find(MangaId) is not { } manga) return NotFound(nameof(MangaId)); + context.Entry(manga).Collection(m => m.Chapters).Load(); + List chapters = manga.Chapters.Where(c => c.Downloaded == false).ToList(); if (chapters.Count == 0) return NoContent(); @@ -243,6 +271,8 @@ public class MangaController(MangaContext context) : Controller if (context.Mangas.Find(MangaId) is not { } manga) return NotFound(nameof(MangaId)); + context.Entry(manga).Collection(m => m.Chapters).Load(); + List chapters = manga.Chapters.ToList(); if (chapters.Count == 0) { @@ -258,6 +288,10 @@ public class MangaController(MangaContext context) : Controller if (max is null) return StatusCode(Status500InternalServerError, "Max chapter could not be found"); + foreach (CollectionEntry collectionEntry in context.Entry(max).Collections) + collectionEntry.Load(); + context.Entry(max).Navigation(nameof(Chapter.ParentManga)).Load(); + return Ok(max); } @@ -281,6 +315,8 @@ public class MangaController(MangaContext context) : Controller if (context.Mangas.Find(MangaId) is not { } manga) return NotFound(nameof(MangaId)); + context.Entry(manga).Collection(m => m.Chapters).Load(); + List chapters = manga.Chapters.ToList(); if (chapters.Count == 0) { @@ -296,6 +332,10 @@ public class MangaController(MangaContext context) : Controller if (max is null) return StatusCode(Status412PreconditionFailed, "Max chapter could not be found"); + foreach (CollectionEntry collectionEntry in context.Entry(max).Collections) + collectionEntry.Load(); + context.Entry(max).Navigation(nameof(Chapter.ParentManga)).Load(); + return Ok(max); } @@ -333,12 +373,16 @@ public class MangaController(MangaContext context) : Controller [HttpPost("{MangaId}/ChangeLibrary/{LibraryId}")] [ProducesResponseType(Status202Accepted)] [ProducesResponseType(Status404NotFound)] - public IActionResult MoveFolder(string MangaId, string LibraryId) + public IActionResult ChangeLibrary(string MangaId, string LibraryId) { if (context.Mangas.Find(MangaId) is not { } manga) return NotFound(nameof(MangaId)); if(context.FileLibraries.Find(LibraryId) is not { } library) return NotFound(nameof(LibraryId)); + + foreach (CollectionEntry collectionEntry in context.Entry(manga).Collections) + collectionEntry.Load(); + context.Entry(manga).Navigation(nameof(Manga.Library)).Load(); MoveMangaLibraryWorker moveLibrary = new(manga, library); @@ -371,15 +415,20 @@ public class MangaController(MangaContext context) : Controller if(Tranga.MangaConnectors.FirstOrDefault(c => c.Name.Equals(MangaConnectorName, StringComparison.InvariantCultureIgnoreCase)) is not { } connector) return NotFound(nameof(MangaConnectorName)); - if (context.MangaConnectorToManga.FirstOrDefault(id => id.MangaConnectorName == MangaConnectorName && id.ObjId == MangaId) is not { } mcId) + if (context.MangaConnectorToManga + .FirstOrDefault(id => id.MangaConnectorName == MangaConnectorName && id.ObjId == MangaId) + is not { } mcId) + { if(IsRequested) return StatusCode(Status428PreconditionRequired, "Don't know how to download this Manga from MangaConnector"); else return StatusCode(Status412PreconditionFailed, "Not linked anyways."); + } mcId.UseForDownload = IsRequested; if(context.Sync() is { success: false } result) return StatusCode(Status500InternalServerError, result.exceptionMessage); + DownloadCoverFromMangaconnectorWorker downloadCover = new(mcId); RetrieveMangaChaptersFromMangaconnectorWorker retrieveChapters = new(mcId, Tranga.Settings.DownloadLanguage); diff --git a/API/Schema/MangaContext/Chapter.cs b/API/Schema/MangaContext/Chapter.cs index 27cc16d..1be326b 100644 --- a/API/Schema/MangaContext/Chapter.cs +++ b/API/Schema/MangaContext/Chapter.cs @@ -4,7 +4,6 @@ using System.Text; using System.Text.RegularExpressions; using System.Xml.Linq; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; using Newtonsoft.Json; namespace API.Schema.MangaContext; @@ -15,8 +14,7 @@ public class Chapter : Identifiable, IComparable [StringLength(64)] [Required] public string ParentMangaId { get; init; } = null!; [JsonIgnore] public Manga ParentManga = null!; - [NotMapped] - public Dictionary IdsOnMangaConnectors => + [NotMapped] public Dictionary IdsOnMangaConnectors => MangaConnectorIds.ToDictionary(id => id.MangaConnectorName, id => id.IdOnConnectorSite); [JsonIgnore] public ICollection> MangaConnectorIds = null!; diff --git a/API/Schema/MangaContext/Manga.cs b/API/Schema/MangaContext/Manga.cs index c50e410..f474977 100644 --- a/API/Schema/MangaContext/Manga.cs +++ b/API/Schema/MangaContext/Manga.cs @@ -20,9 +20,9 @@ public class Manga : Identifiable [StringLength(64)] public string? LibraryId { get; private set; } [JsonIgnore] public FileLibrary? Library = null!; - public ICollection Authors { get; internal set; }= null!; - public ICollection MangaTags { get; internal set; }= null!; - public ICollection Links { get; internal set; }= null!; + public ICollection Authors { get; internal set; } = null!; + public ICollection MangaTags { get; internal set; } = null!; + public ICollection Links { get; internal set; } = null!; public ICollection AltTitles { get; internal set; } = null!; [Required] public float IgnoreChaptersBefore { get; internal set; } [StringLength(1024)] [Required] public string DirectoryName { get; private set; } diff --git a/API/Schema/MangaContext/MetadataFetchers/MyAnimeList.cs b/API/Schema/MangaContext/MetadataFetchers/MyAnimeList.cs index 1816d89..1737ee4 100644 --- a/API/Schema/MangaContext/MetadataFetchers/MyAnimeList.cs +++ b/API/Schema/MangaContext/MetadataFetchers/MyAnimeList.cs @@ -1,6 +1,7 @@ using System.Text.RegularExpressions; using JikanDotNet; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; namespace API.Schema.MangaContext.MetadataFetchers; @@ -47,6 +48,11 @@ public class MyAnimeList : MetadataFetcher public override void UpdateMetadata(MetadataEntry metadataEntry, MangaContext dbContext) { Manga dbManga = dbContext.Mangas.Find(metadataEntry.MangaId)!; + + foreach (CollectionEntry collectionEntry in dbContext.Entry(dbManga).Collections) + collectionEntry.Load(); + dbContext.Entry(dbManga).Navigation(nameof(Manga.Library)).Load(); + MangaFull resultData; try { diff --git a/API/Workers/MangaDownloadWorkers/DownloadChapterFromMangaconnectorWorker.cs b/API/Workers/MangaDownloadWorkers/DownloadChapterFromMangaconnectorWorker.cs index 64d39ff..8c05c5b 100644 --- a/API/Workers/MangaDownloadWorkers/DownloadChapterFromMangaconnectorWorker.cs +++ b/API/Workers/MangaDownloadWorkers/DownloadChapterFromMangaconnectorWorker.cs @@ -3,6 +3,7 @@ using System.Runtime.InteropServices; using API.MangaConnectors; using API.MangaDownloadClients; using API.Schema.MangaContext; +using Microsoft.EntityFrameworkCore.ChangeTracking; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Processing; @@ -21,6 +22,8 @@ public class DownloadChapterFromMangaconnectorWorker(MangaConnectorId c return []; //TODO Exception? if (!Tranga.TryGetMangaConnector(mangaConnectorId.MangaConnectorName, out MangaConnector? mangaConnector)) return []; //TODO Exception? + + DbContext.Entry(mangaConnectorId).Navigation(nameof(MangaConnectorId.Obj)).Load(); Chapter chapter = mangaConnectorId.Obj; if (chapter.Downloaded) { @@ -28,6 +31,7 @@ public class DownloadChapterFromMangaconnectorWorker(MangaConnectorId c return []; } + DbContext.Entry(chapter).Navigation(nameof(Chapter.ParentManga)).Load(); string[] imageUrls = mangaConnector.GetChapterImageUrls(mangaConnectorId); if (imageUrls.Length < 1) { @@ -83,6 +87,9 @@ public class DownloadChapterFromMangaconnectorWorker(MangaConnectorId c CopyCoverFromCacheToDownloadLocation(chapter.ParentManga); Log.Debug($"Creating ComicInfo.xml {chapter}"); + foreach (CollectionEntry collectionEntry in DbContext.Entry(chapter.ParentManga).Collections) + collectionEntry.Load(); + DbContext.Entry(chapter.ParentManga).Navigation(nameof(Manga.Library)).Load(); File.WriteAllText(Path.Join(tempFolder, "ComicInfo.xml"), chapter.GetComicInfoXmlString()); Log.Debug($"Packaging images to archive {chapter}"); @@ -148,6 +155,7 @@ public class DownloadChapterFromMangaconnectorWorker(MangaConnectorId c } //TODO MangaConnector Selection + DbContext.Entry(manga).Collection(m => m.MangaConnectorIds).Load(); MangaConnectorId mangaConnectorId = manga.MangaConnectorIds.First(); if (!Tranga.TryGetMangaConnector(mangaConnectorId.MangaConnectorName, out MangaConnector? mangaConnector)) { @@ -156,6 +164,7 @@ public class DownloadChapterFromMangaconnectorWorker(MangaConnectorId c } Log.Info($"Copying cover to {publicationFolder}"); + DbContext.Entry(mangaConnectorId).Navigation(nameof(MangaConnectorId.Obj)).Load(); string? fileInCache = manga.CoverFileNameInCache ?? mangaConnector.SaveCoverImageToCache(mangaConnectorId); if (fileInCache is null) { diff --git a/API/Workers/MangaDownloadWorkers/DownloadCoverFromMangaconnectorWorker.cs b/API/Workers/MangaDownloadWorkers/DownloadCoverFromMangaconnectorWorker.cs index bdae3e2..194cbea 100644 --- a/API/Workers/MangaDownloadWorkers/DownloadCoverFromMangaconnectorWorker.cs +++ b/API/Workers/MangaDownloadWorkers/DownloadCoverFromMangaconnectorWorker.cs @@ -13,6 +13,8 @@ public class DownloadCoverFromMangaconnectorWorker(MangaConnectorId mcId, return []; //TODO Exception? if (!Tranga.TryGetMangaConnector(mangaConnectorId.MangaConnectorName, out MangaConnector? mangaConnector)) return []; //TODO Exception? + + DbContext.Entry(mangaConnectorId).Navigation(nameof(MangaConnectorId.Obj)).Load(); Manga manga = mangaConnectorId.Obj; manga.CoverFileNameInCache = mangaConnector.SaveCoverImageToCache(mangaConnectorId); diff --git a/API/Workers/MangaDownloadWorkers/RetrieveMangaChaptersFromMangaconnectorWorker.cs b/API/Workers/MangaDownloadWorkers/RetrieveMangaChaptersFromMangaconnectorWorker.cs index 65c462b..716aad3 100644 --- a/API/Workers/MangaDownloadWorkers/RetrieveMangaChaptersFromMangaconnectorWorker.cs +++ b/API/Workers/MangaDownloadWorkers/RetrieveMangaChaptersFromMangaconnectorWorker.cs @@ -13,7 +13,11 @@ public class RetrieveMangaChaptersFromMangaconnectorWorker(MangaConnectorId.Obj)).Load(); Manga manga = mangaConnectorId.Obj; + DbContext.Entry(manga).Collection(m => m.Chapters).Load(); + // This gets all chapters that are not downloaded (Chapter, MangaConnectorId)[] allChapters = mangaConnector.GetChapters(mangaConnectorId, language).DistinctBy(c => c.Item1.Key).ToArray(); diff --git a/API/Workers/MoveMangaLibraryWorker.cs b/API/Workers/MoveMangaLibraryWorker.cs index 4367443..255c43b 100644 --- a/API/Workers/MoveMangaLibraryWorker.cs +++ b/API/Workers/MoveMangaLibraryWorker.cs @@ -13,6 +13,10 @@ public class MoveMangaLibraryWorker(Manga manga, FileLibrary toLibrary, IEnumera return []; //TODO Exception? if (DbContext.FileLibraries.Find(LibraryId) is not { } toLibrary) return []; //TODO Exception? + + DbContext.Entry(manga).Collection(m => m.Chapters).Load(); + DbContext.Entry(manga).Navigation(nameof(Manga.Library)).Load(); + Dictionary oldPath = manga.Chapters.ToDictionary(c => c, c => c.FullArchiveFilePath); manga.Library = toLibrary; diff --git a/API/Workers/PeriodicWorkers/CheckForNewChaptersWorker.cs b/API/Workers/PeriodicWorkers/CheckForNewChaptersWorker.cs index 2f12557..5866e5c 100644 --- a/API/Workers/PeriodicWorkers/CheckForNewChaptersWorker.cs +++ b/API/Workers/PeriodicWorkers/CheckForNewChaptersWorker.cs @@ -1,4 +1,5 @@ using API.Schema.MangaContext; +using Microsoft.EntityFrameworkCore; namespace API.Workers; @@ -10,7 +11,9 @@ public class CheckForNewChaptersWorker(TimeSpan? interval = null, IEnumerable> connectorIdsManga = DbContext.MangaConnectorToManga.Where(id => id.UseForDownload); + IQueryable> connectorIdsManga = DbContext.MangaConnectorToManga + .Include(id => id.Obj) + .Where(id => id.UseForDownload); List newWorkers = new(); foreach (MangaConnectorId mangaConnectorId in connectorIdsManga) diff --git a/API/Workers/PeriodicWorkers/UpdateChaptersDownloadedWorker.cs b/API/Workers/PeriodicWorkers/UpdateChaptersDownloadedWorker.cs index 3d7e985..9248b3e 100644 --- a/API/Workers/PeriodicWorkers/UpdateChaptersDownloadedWorker.cs +++ b/API/Workers/PeriodicWorkers/UpdateChaptersDownloadedWorker.cs @@ -1,4 +1,6 @@ using API.Schema.MangaContext; +using Microsoft.EntityFrameworkCore; + namespace API.Workers; public class UpdateChaptersDownloadedWorker(TimeSpan? interval = null, IEnumerable? dependsOn = null) @@ -8,7 +10,7 @@ public class UpdateChaptersDownloadedWorker(TimeSpan? interval = null, IEnumerab public TimeSpan Interval { get; set; } = interval??TimeSpan.FromMinutes(60); protected override BaseWorker[] DoWorkInternal() { - foreach (Chapter dbContextChapter in DbContext.Chapters) + foreach (Chapter dbContextChapter in DbContext.Chapters.Include(c => c.ParentManga)) dbContextChapter.Downloaded = dbContextChapter.CheckDownloaded(); DbContext.Sync();