diff --git a/API/Schema/Contexts/PgsqlContext.cs b/API/Schema/Contexts/PgsqlContext.cs index 594c9c9..8fc0b8a 100644 --- a/API/Schema/Contexts/PgsqlContext.cs +++ b/API/Schema/Contexts/PgsqlContext.cs @@ -118,7 +118,8 @@ public class PgsqlContext(DbContextOptions options) : DbContext(op .WithMany(); modelBuilder.Entity() .Navigation(j => j.DependsOnJobs) - .AutoInclude(false); + .AutoInclude(false) + .EnableLazyLoading(); //MangaConnector Types modelBuilder.Entity() @@ -149,7 +150,8 @@ public class PgsqlContext(DbContextOptions options) : DbContext(op .AutoInclude(); modelBuilder.Entity() .Navigation(m => m.Chapters) - .AutoInclude(false); + .AutoInclude(false) + .EnableLazyLoading(); //Manga owns MangaAltTitles modelBuilder.Entity() .OwnsMany(m => m.AltTitles) diff --git a/API/Schema/Jobs/DownloadAvailableChaptersJob.cs b/API/Schema/Jobs/DownloadAvailableChaptersJob.cs index cffd60f..961cb3a 100644 --- a/API/Schema/Jobs/DownloadAvailableChaptersJob.cs +++ b/API/Schema/Jobs/DownloadAvailableChaptersJob.cs @@ -36,7 +36,6 @@ public class DownloadAvailableChaptersJob : Job protected override IEnumerable RunInternal(PgsqlContext context) { - context.Entry(Manga).Collection(m => m.Chapters).Load(); return Manga.Chapters.Select(chapter => new DownloadSingleChapterJob(chapter, this)); } } \ No newline at end of file diff --git a/API/Schema/Jobs/Job.cs b/API/Schema/Jobs/Job.cs index cf2b5d9..ffcb227 100644 --- a/API/Schema/Jobs/Job.cs +++ b/API/Schema/Jobs/Job.cs @@ -17,7 +17,12 @@ public abstract class Job [StringLength(64)] public string? ParentJobId { get; init; } [JsonIgnore] public Job? ParentJob { get; init; } - [JsonIgnore] public ICollection DependsOnJobs { get; init; } + private ICollection _dependsOnJobs = null!; + [JsonIgnore] public ICollection DependsOnJobs + { + get => LazyLoader.Load(this, ref _dependsOnJobs); + init => _dependsOnJobs = value; + } [Required] public JobType JobType { get; init; } @@ -68,41 +73,31 @@ public abstract class Job DateTime jobStart = DateTime.UtcNow; Job[]? ret = null; + using IServiceScope scope = serviceProvider.CreateScope(); + PgsqlContext context = scope.ServiceProvider.GetRequiredService(); try { - - using IServiceScope scope = serviceProvider.CreateScope(); - PgsqlContext context = scope.ServiceProvider.GetRequiredService(); - try - { - context.Attach(this); - this.state = JobState.Running; - context.SaveChanges(); - ret = RunInternal(context).ToArray(); - this.state = JobState.Completed; - context.Jobs.AddRange(ret); - Log.Info($"Job {JobId} completed. Generated {ret.Length} new jobs."); - } - catch (Exception e) - { - if (e is not DbUpdateException dbEx) - { - this.state = JobState.Failed; - Log.Error($"Failed to run job {JobId}", e); - } - else - { - throw; - } - } - finally - { - context.SaveChanges(); - } + context.Attach(this); + this.state = JobState.Running; + context.SaveChanges(); + ret = RunInternal(context).ToArray(); + this.state = JobState.Completed; + context.Jobs.AddRange(ret); + Log.Info($"Job {JobId} completed. Generated {ret.Length} new jobs."); + context.SaveChanges(); } - catch (DbUpdateException e) + catch (Exception e) { - Log.Error($"Failed to update Database {JobId}", e); + if (e is not DbUpdateException) + { + this.state = JobState.Failed; + Log.Error($"Failed to run job {JobId}", e); + context.SaveChanges(); + } + else + { + Log.Error($"Failed to update Database {JobId}", e); + } } Log.Info($"Finished Job {JobId}! (took {DateTime.UtcNow.Subtract(jobStart).TotalMilliseconds}ms)"); diff --git a/API/Schema/Jobs/MoveMangaLibraryJob.cs b/API/Schema/Jobs/MoveMangaLibraryJob.cs index 2b044c1..365ee1f 100644 --- a/API/Schema/Jobs/MoveMangaLibraryJob.cs +++ b/API/Schema/Jobs/MoveMangaLibraryJob.cs @@ -42,8 +42,6 @@ public class MoveMangaLibraryJob : Job protected override IEnumerable RunInternal(PgsqlContext context) { - context.Attach(Manga); - context.Entry(Manga).Collection(m => m.Chapters).Load(); Dictionary oldPath = Manga.Chapters.ToDictionary(c => c, c => c.FullArchiveFilePath); Manga.Library = ToLibrary; try diff --git a/API/Schema/Jobs/RetrieveChaptersJob.cs b/API/Schema/Jobs/RetrieveChaptersJob.cs index 95866fc..56ee642 100644 --- a/API/Schema/Jobs/RetrieveChaptersJob.cs +++ b/API/Schema/Jobs/RetrieveChaptersJob.cs @@ -40,7 +40,6 @@ public class RetrieveChaptersJob : Job protected override IEnumerable RunInternal(PgsqlContext context) { - context.Entry(Manga).Collection(m => m.Chapters).Load(); // This gets all chapters that are not downloaded Chapter[] allChapters = Manga.MangaConnector.GetChapters(Manga, Language); Chapter[] newChapters = allChapters.Where(chapter => Manga.Chapters.Contains(chapter) == false).ToArray(); diff --git a/API/Schema/Jobs/UpdateChaptersDownloadedJob.cs b/API/Schema/Jobs/UpdateChaptersDownloadedJob.cs index b05d874..41228ea 100644 --- a/API/Schema/Jobs/UpdateChaptersDownloadedJob.cs +++ b/API/Schema/Jobs/UpdateChaptersDownloadedJob.cs @@ -36,7 +36,6 @@ public class UpdateChaptersDownloadedJob : Job protected override IEnumerable RunInternal(PgsqlContext context) { - context.Entry(Manga).Collection(m => m.Chapters).Load(); return Manga.Chapters.Select(c => new UpdateSingleChapterDownloadedJob(c, this)); } } \ No newline at end of file diff --git a/API/Schema/Manga.cs b/API/Schema/Manga.cs index 2620193..d2daf14 100644 --- a/API/Schema/Manga.cs +++ b/API/Schema/Manga.cs @@ -4,6 +4,7 @@ using System.Runtime.InteropServices; using System.Text; using API.Schema.MangaConnectors; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; using Newtonsoft.Json; using static System.IO.UnixFileMode; @@ -45,8 +46,16 @@ public class Manga [JsonIgnore] [NotMapped] public string? FullDirectoryPath => Library is not null ? Path.Join(Library.BasePath, DirectoryName) : null; - - [JsonIgnore] public ICollection Chapters { get; internal set; } = []; + + [NotMapped] public ICollection ChapterIds => Chapters.Select(c => c.ChapterId).ToList(); + private readonly ILazyLoader _lazyLoader = null!; + private ICollection _chapters = null!; + [JsonIgnore] + public ICollection Chapters + { + get => _lazyLoader.Load(this, ref _chapters); + init => _chapters = value; + } public Manga(string idOnConnector, string name, string description, string websiteUrl, string coverUrl, MangaReleaseStatus releaseStatus, MangaConnector mangaConnector, ICollection authors, ICollection mangaTags, ICollection links, ICollection altTitles, @@ -71,14 +80,16 @@ public class Manga this.DirectoryName = CleanDirectoryName(name); this.Year = year; this.OriginalLanguage = originalLanguage; + this.Chapters = []; } /// /// EF ONLY!!! /// - public Manga(string mangaId, string idOnConnectorSite, string name, string description, string websiteUrl, string coverUrl, MangaReleaseStatus releaseStatus, + public Manga(ILazyLoader lazyLoader, string mangaId, string idOnConnectorSite, string name, string description, string websiteUrl, string coverUrl, MangaReleaseStatus releaseStatus, string mangaConnectorName, string directoryName, float ignoreChaptersBefore, string? libraryId, uint? year, string? originalLanguage) { + this._lazyLoader = lazyLoader; this.MangaId = mangaId; this.IdOnConnectorSite = idOnConnectorSite; this.Name = name;