Files
manga-reader/MangaReader.Core/Data/MangaContext.cs

409 lines
13 KiB
C#

using Microsoft.EntityFrameworkCore;
namespace MangaReader.Core.Data;
public class MangaContext(DbContextOptions options) : DbContext(options)
{
public DbSet<Manga> Mangas { get; set; }
public DbSet<MangaCover> MangaCovers { get; set; }
public DbSet<MangaTitle> MangaTitles { get; set; }
public DbSet<MangaDescription> MangaDescriptions { get; set; }
public DbSet<Source> Sources { get; set; }
public DbSet<MangaSource> MangaSources { get; set; }
public DbSet<Contributor> Contributors { get; set; }
public DbSet<MangaContributor> MangaContributors { get; set; }
public DbSet<Genre> Genres { get; set; }
public DbSet<MangaGenre> MangaGenres { get; set; }
//public DbSet<MangaChapter> MangaChapters { get; set; }
//public DbSet<ChapterSource> ChapterSources { get; set; }
//public DbSet<ChapterPage> ChapterPages { get; set; }
public DbSet<SourceTitle> SourceTitles { get; set; }
public DbSet<SourceDescription> SourceDescriptions { get; set; }
public DbSet<SourceCover> SourceCovers { get; set; }
public DbSet<SourceChapter> SourceChapters { get; set; }
public DbSet<SourcePage> SourcePages { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
ConfigureManga(modelBuilder);
ConfigureMangaCover(modelBuilder);
ConfigureMangaTitle(modelBuilder);
ConfigureMangaDescription(modelBuilder);
ConfigureSource(modelBuilder);
ConfigureMangaSource(modelBuilder);
ConfigureContributor(modelBuilder);
ConfigureMangaContributor(modelBuilder);
ConfigureGenre(modelBuilder);
ConfigureMangaGenre(modelBuilder);
//ConfigureMangaChapter(modelBuilder);
//ConfigureChapterSource(modelBuilder);
//ConfigureChapterPage(modelBuilder);
ConfigureSourceTitle(modelBuilder);
ConfigureSourceDescription(modelBuilder);
ConfigureMangaSourceCover(modelBuilder);
ConfigureMangaSourceChapter(modelBuilder);
ConfigureSourcePage(modelBuilder);
}
private static void ConfigureManga(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Manga>()
.HasKey(x => x.MangaId);
//modelBuilder.Entity<Manga>()
// .HasIndex(x => x.Title)
// .IsUnique();
modelBuilder.Entity<Manga>()
.HasIndex(x => x.Slug)
.IsUnique();
}
private static void ConfigureMangaCover(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<MangaCover>()
.HasKey(x => x.MangaCoverId);
modelBuilder
.Entity<MangaCover>()
.HasOne(x => x.Manga)
.WithMany(x => x.Covers)
.HasForeignKey(x => x.MangaId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<MangaCover>()
.HasIndex(x => x.Guid)
.IsUnique();
//modelBuilder
// .Entity<MangaCover>()
// .HasIndex(x => new { x.MangaId, x.IsPrimary })
// .IsUnique()
// .HasFilter("[IsPrimary] = 1"); // Enforce only one primary cover per manga
}
private static void ConfigureMangaTitle(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<MangaTitle>()
.HasKey(mangaTitle => mangaTitle.MangaTitleId);
modelBuilder.Entity<MangaTitle>()
.Property(mt => mt.Name)
.IsRequired();
modelBuilder.Entity<MangaTitle>()
.Property(mt => mt.Language)
.IsRequired();
modelBuilder.Entity<MangaTitle>()
.HasIndex(mangaTitle => new { mangaTitle.MangaId, mangaTitle.Name, mangaTitle.Language })
.IsUnique();
modelBuilder
.Entity<MangaTitle>()
.HasIndex(mangaTitle => mangaTitle.Name);
modelBuilder
.Entity<MangaTitle>()
.HasOne(x => x.Manga)
.WithMany(x => x.Titles)
.HasForeignKey(x => x.MangaId)
.OnDelete(DeleteBehavior.Cascade);
}
private static void ConfigureMangaDescription(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<MangaDescription>()
.HasKey(mangaDescription => mangaDescription.MangaDescriptionId);
modelBuilder.Entity<MangaDescription>()
.Property(mangaDescription => mangaDescription.Text)
.IsRequired();
modelBuilder.Entity<MangaDescription>()
.Property(mangaDescription => mangaDescription.Language)
.IsRequired();
modelBuilder.Entity<MangaDescription>()
.HasIndex(mangaDescription => new { mangaDescription.MangaId, mangaDescription.Text, mangaDescription.Language })
.IsUnique();
modelBuilder
.Entity<MangaDescription>()
.HasIndex(mangaDescription => mangaDescription.Text);
modelBuilder
.Entity<MangaDescription>()
.HasOne(x => x.Manga)
.WithMany(x => x.Descriptions)
.HasForeignKey(x => x.MangaId)
.OnDelete(DeleteBehavior.Cascade);
}
private static void ConfigureSource(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Source>()
.HasKey(x => x.SourceId);
modelBuilder
.Entity<Source>()
.HasIndex(x => x.Name)
.IsUnique(true);
}
private static void ConfigureMangaSource(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<MangaSource>()
.HasKey(mangaSource => mangaSource.MangaSourceId);
//modelBuilder
// .Entity<MangaSource>()
// .HasKey(mangaSource => new { mangaSource.MangaId, mangaSource.SourceId });
modelBuilder.Entity<MangaSource>()
.HasIndex(x => x.Url)
.IsUnique();
modelBuilder
.Entity<MangaSource>()
.HasOne(x => x.Manga)
.WithMany(x => x.Sources)
.HasForeignKey(x => x.MangaId)
.OnDelete(DeleteBehavior.Cascade);
}
private static void ConfigureContributor(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Contributor>()
.HasKey(x => x.ContributorId);
modelBuilder
.Entity<Contributor>()
.HasIndex(x => x.Name)
.IsUnique(true);
}
private static void ConfigureMangaContributor(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<MangaContributor>()
.HasKey(mc => new { mc.MangaId, mc.ContributorId, mc.Role });
modelBuilder
.Entity<MangaContributor>()
.HasOne(x => x.Manga)
.WithMany(x => x.Contributors)
.HasForeignKey(x => x.MangaId)
.OnDelete(DeleteBehavior.Cascade);
}
private static void ConfigureGenre(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Genre>()
.HasKey(x => x.GenreId);
modelBuilder
.Entity<Genre>()
.HasIndex(x => x.Name)
.IsUnique(true);
}
private static void ConfigureMangaGenre(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<MangaGenre>()
.HasKey(mangaGenre => new { mangaGenre.MangaId, mangaGenre.GenreId });
modelBuilder
.Entity<MangaGenre>()
.HasOne(x => x.Manga)
.WithMany(x => x.Genres)
.HasForeignKey(x => x.MangaId)
.OnDelete(DeleteBehavior.Cascade);
}
//private static void ConfigureMangaChapter(ModelBuilder modelBuilder)
//{
// modelBuilder
// .Entity<MangaChapter>()
// .HasKey(x => x.MangaChapterId);
// modelBuilder
// .Entity<MangaChapter>()
// .HasOne(x => x.Manga)
// .WithMany(x => x.Chapters)
// .HasForeignKey(x => x.MangaId)
// .OnDelete(DeleteBehavior.Cascade);
//}
//private static void ConfigureChapterSource(ModelBuilder modelBuilder)
//{
// modelBuilder
// .Entity<ChapterSource>()
// .HasKey(chapterSource => new { chapterSource.MangaChapterId, chapterSource.SourceId });
// modelBuilder
// .Entity<ChapterSource>()
// .HasOne(x => x.Chapter)
// .WithMany(x => x.Sources)
// .HasForeignKey(x => x.MangaChapterId)
// .OnDelete(DeleteBehavior.Cascade);
//}
//private static void ConfigureChapterPage(ModelBuilder modelBuilder)
//{
// modelBuilder
// .Entity<ChapterPage>()
// .HasKey(chapterPage => chapterPage.ChapterPageId);
// modelBuilder
// .Entity<ChapterPage>()
// .HasIndex(chapterPage => new { chapterPage.MangaChapterId, chapterPage.PageNumber })
// .IsUnique(true);
// modelBuilder
// .Entity<ChapterPage>()
// .HasOne(x => x.MangaChapter)
// .WithMany(x => x.Pages)
// .HasForeignKey(x => x.MangaChapterId)
// .OnDelete(DeleteBehavior.Cascade);
//}
private static void ConfigureSourceTitle(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<SourceTitle>()
.HasKey(x => x.SourceTitleId);
modelBuilder
.Entity<SourceTitle>()
.Property(x => x.Name)
.IsRequired()
.HasMaxLength(512);
// Avoid duplicate rows coming from the same source record
modelBuilder
.Entity<SourceTitle>()
.HasIndex(x => new { x.MangaSourceId, x.Language, x.Name })
.IsUnique();
modelBuilder
.Entity<SourceTitle>()
.HasOne(x => x.MangaSource)
.WithMany(x => x.Titles)
.HasForeignKey(x => x.MangaSourceId)
.OnDelete(DeleteBehavior.Cascade);
}
private static void ConfigureSourceDescription(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<SourceDescription>()
.HasKey(x => x.SourceDescriptionId);
modelBuilder
.Entity<SourceDescription>()
.Property(x => x.Text)
.IsRequired();
// If sources can emit multiple descriptions per language, keep it non-unique.
// If not, uncomment:
//modelBuilder
// .Entity<SourceDescription>()
// .HasIndex(x => new { x.MangaSourceId, x.Language })
// .IsUnique();
modelBuilder
.Entity<SourceDescription>()
.HasOne(x => x.MangaSource)
.WithMany(x => x.Descriptions)
.HasForeignKey(x => x.MangaSourceId)
.OnDelete(DeleteBehavior.Cascade);
}
private static void ConfigureMangaSourceCover(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<SourceCover>()
.HasKey(sourceCover => sourceCover.SourceCoverId);
modelBuilder
.Entity<SourceCover>()
.Property(x => x.Url)
.IsRequired()
.HasMaxLength(2048);
modelBuilder
.Entity<SourceCover>()
.HasIndex(sourceCover => new { sourceCover.MangaSourceId, sourceCover.Url })
.IsUnique(true);
modelBuilder
.Entity<SourceCover>()
.HasOne(x => x.MangaSource)
.WithMany(x => x.Covers)
.HasForeignKey(x => x.MangaSourceId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder
.Entity<SourceCover>()
.HasOne(x => x.MangaCover)
.WithMany() // or .WithMany(c => c.SourceCovers) if you add a collection nav on MangaCover
.HasForeignKey(x => x.MangaCoverId)
.OnDelete(DeleteBehavior.SetNull);
// Helpful for lookups when you dedupe/file-link after download
modelBuilder
.Entity<SourceCover>()
.HasIndex(x => x.MangaCoverId);
}
private static void ConfigureMangaSourceChapter(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<SourceChapter>()
.HasKey(sourceChapter => sourceChapter.SourceChapterId);
modelBuilder
.Entity<SourceChapter>()
.HasIndex(sourceChapter => new { sourceChapter.MangaSourceId, sourceChapter.ChapterNumber, sourceChapter.Url })
.IsUnique(true);
modelBuilder
.Entity<SourceChapter>()
.HasOne(x => x.MangaSource)
.WithMany(x => x.Chapters)
.HasForeignKey(x => x.MangaSourceId)
.OnDelete(DeleteBehavior.Cascade);
}
private static void ConfigureSourcePage(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<SourcePage>()
.HasKey(sourcePage => sourcePage.SourcePageId);
modelBuilder
.Entity<SourcePage>()
.HasIndex(sourcePage => new { sourcePage.SourceChapterId, sourcePage.PageNumber })
.IsUnique(true);
modelBuilder
.Entity<SourcePage>()
.HasOne(x => x.Chapter)
.WithMany(x => x.Pages)
.HasForeignKey(x => x.SourceChapterId)
.OnDelete(DeleteBehavior.Cascade);
}
}