Lazy Load Jobs.DependsOnJobs, Manga.Chapters

This commit is contained in:
Glax 2025-05-16 21:53:59 +02:00
parent 6258e07f20
commit 225b7f02ad
7 changed files with 45 additions and 42 deletions

View File

@ -118,7 +118,8 @@ public class PgsqlContext(DbContextOptions<PgsqlContext> options) : DbContext(op
.WithMany(); .WithMany();
modelBuilder.Entity<Job>() modelBuilder.Entity<Job>()
.Navigation(j => j.DependsOnJobs) .Navigation(j => j.DependsOnJobs)
.AutoInclude(false); .AutoInclude(false)
.EnableLazyLoading();
//MangaConnector Types //MangaConnector Types
modelBuilder.Entity<MangaConnector>() modelBuilder.Entity<MangaConnector>()
@ -149,7 +150,8 @@ public class PgsqlContext(DbContextOptions<PgsqlContext> options) : DbContext(op
.AutoInclude(); .AutoInclude();
modelBuilder.Entity<Manga>() modelBuilder.Entity<Manga>()
.Navigation(m => m.Chapters) .Navigation(m => m.Chapters)
.AutoInclude(false); .AutoInclude(false)
.EnableLazyLoading();
//Manga owns MangaAltTitles //Manga owns MangaAltTitles
modelBuilder.Entity<Manga>() modelBuilder.Entity<Manga>()
.OwnsMany<MangaAltTitle>(m => m.AltTitles) .OwnsMany<MangaAltTitle>(m => m.AltTitles)

View File

@ -36,7 +36,6 @@ public class DownloadAvailableChaptersJob : Job
protected override IEnumerable<Job> RunInternal(PgsqlContext context) protected override IEnumerable<Job> RunInternal(PgsqlContext context)
{ {
context.Entry(Manga).Collection<Chapter>(m => m.Chapters).Load();
return Manga.Chapters.Select(chapter => new DownloadSingleChapterJob(chapter, this)); return Manga.Chapters.Select(chapter => new DownloadSingleChapterJob(chapter, this));
} }
} }

View File

@ -17,7 +17,12 @@ public abstract class Job
[StringLength(64)] public string? ParentJobId { get; init; } [StringLength(64)] public string? ParentJobId { get; init; }
[JsonIgnore] public Job? ParentJob { get; init; } [JsonIgnore] public Job? ParentJob { get; init; }
[JsonIgnore] public ICollection<Job> DependsOnJobs { get; init; } private ICollection<Job> _dependsOnJobs = null!;
[JsonIgnore] public ICollection<Job> DependsOnJobs
{
get => LazyLoader.Load(this, ref _dependsOnJobs);
init => _dependsOnJobs = value;
}
[Required] public JobType JobType { get; init; } [Required] public JobType JobType { get; init; }
@ -68,41 +73,31 @@ public abstract class Job
DateTime jobStart = DateTime.UtcNow; DateTime jobStart = DateTime.UtcNow;
Job[]? ret = null; Job[]? ret = null;
using IServiceScope scope = serviceProvider.CreateScope();
PgsqlContext context = scope.ServiceProvider.GetRequiredService<PgsqlContext>();
try try
{ {
context.Attach(this);
using IServiceScope scope = serviceProvider.CreateScope(); this.state = JobState.Running;
PgsqlContext context = scope.ServiceProvider.GetRequiredService<PgsqlContext>(); context.SaveChanges();
try ret = RunInternal(context).ToArray();
{ this.state = JobState.Completed;
context.Attach(this); context.Jobs.AddRange(ret);
this.state = JobState.Running; Log.Info($"Job {JobId} completed. Generated {ret.Length} new jobs.");
context.SaveChanges(); 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();
}
} }
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)"); Log.Info($"Finished Job {JobId}! (took {DateTime.UtcNow.Subtract(jobStart).TotalMilliseconds}ms)");

View File

@ -42,8 +42,6 @@ public class MoveMangaLibraryJob : Job
protected override IEnumerable<Job> RunInternal(PgsqlContext context) protected override IEnumerable<Job> RunInternal(PgsqlContext context)
{ {
context.Attach(Manga);
context.Entry(Manga).Collection<Chapter>(m => m.Chapters).Load();
Dictionary<Chapter, string> oldPath = Manga.Chapters.ToDictionary(c => c, c => c.FullArchiveFilePath); Dictionary<Chapter, string> oldPath = Manga.Chapters.ToDictionary(c => c, c => c.FullArchiveFilePath);
Manga.Library = ToLibrary; Manga.Library = ToLibrary;
try try

View File

@ -40,7 +40,6 @@ public class RetrieveChaptersJob : Job
protected override IEnumerable<Job> RunInternal(PgsqlContext context) protected override IEnumerable<Job> RunInternal(PgsqlContext context)
{ {
context.Entry(Manga).Collection<Chapter>(m => m.Chapters).Load();
// This gets all chapters that are not downloaded // This gets all chapters that are not downloaded
Chapter[] allChapters = Manga.MangaConnector.GetChapters(Manga, Language); Chapter[] allChapters = Manga.MangaConnector.GetChapters(Manga, Language);
Chapter[] newChapters = allChapters.Where(chapter => Manga.Chapters.Contains(chapter) == false).ToArray(); Chapter[] newChapters = allChapters.Where(chapter => Manga.Chapters.Contains(chapter) == false).ToArray();

View File

@ -36,7 +36,6 @@ public class UpdateChaptersDownloadedJob : Job
protected override IEnumerable<Job> RunInternal(PgsqlContext context) protected override IEnumerable<Job> RunInternal(PgsqlContext context)
{ {
context.Entry(Manga).Collection<Chapter>(m => m.Chapters).Load();
return Manga.Chapters.Select(c => new UpdateSingleChapterDownloadedJob(c, this)); return Manga.Chapters.Select(c => new UpdateSingleChapterDownloadedJob(c, this));
} }
} }

View File

@ -4,6 +4,7 @@ using System.Runtime.InteropServices;
using System.Text; using System.Text;
using API.Schema.MangaConnectors; using API.Schema.MangaConnectors;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Newtonsoft.Json; using Newtonsoft.Json;
using static System.IO.UnixFileMode; using static System.IO.UnixFileMode;
@ -46,7 +47,15 @@ public class Manga
[NotMapped] [NotMapped]
public string? FullDirectoryPath => Library is not null ? Path.Join(Library.BasePath, DirectoryName) : null; public string? FullDirectoryPath => Library is not null ? Path.Join(Library.BasePath, DirectoryName) : null;
[JsonIgnore] public ICollection<Chapter> Chapters { get; internal set; } = []; [NotMapped] public ICollection<string> ChapterIds => Chapters.Select(c => c.ChapterId).ToList();
private readonly ILazyLoader _lazyLoader = null!;
private ICollection<Chapter> _chapters = null!;
[JsonIgnore]
public ICollection<Chapter> Chapters
{
get => _lazyLoader.Load(this, ref _chapters);
init => _chapters = value;
}
public Manga(string idOnConnector, string name, string description, string websiteUrl, string coverUrl, MangaReleaseStatus releaseStatus, public Manga(string idOnConnector, string name, string description, string websiteUrl, string coverUrl, MangaReleaseStatus releaseStatus,
MangaConnector mangaConnector, ICollection<Author> authors, ICollection<MangaTag> mangaTags, ICollection<Link> links, ICollection<MangaAltTitle> altTitles, MangaConnector mangaConnector, ICollection<Author> authors, ICollection<MangaTag> mangaTags, ICollection<Link> links, ICollection<MangaAltTitle> altTitles,
@ -71,14 +80,16 @@ public class Manga
this.DirectoryName = CleanDirectoryName(name); this.DirectoryName = CleanDirectoryName(name);
this.Year = year; this.Year = year;
this.OriginalLanguage = originalLanguage; this.OriginalLanguage = originalLanguage;
this.Chapters = [];
} }
/// <summary> /// <summary>
/// EF ONLY!!! /// EF ONLY!!!
/// </summary> /// </summary>
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) string mangaConnectorName, string directoryName, float ignoreChaptersBefore, string? libraryId, uint? year, string? originalLanguage)
{ {
this._lazyLoader = lazyLoader;
this.MangaId = mangaId; this.MangaId = mangaId;
this.IdOnConnectorSite = idOnConnectorSite; this.IdOnConnectorSite = idOnConnectorSite;
this.Name = name; this.Name = name;