using System; using System.Linq; using System.Threading; using System.Threading.Tasks; using API.Entities; using API.Entities.Enums.UserPreferences; using API.Entities.Interfaces; using API.Entities.Metadata; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; namespace API.Data; public sealed class DataContext : IdentityDbContext, AppUserRole, IdentityUserLogin, IdentityRoleClaim, IdentityUserToken> { public DataContext(DbContextOptions options) : base(options) { ChangeTracker.Tracked += OnEntityTracked; ChangeTracker.StateChanged += OnEntityStateChanged; } public DbSet Library { get; set; } public DbSet Series { get; set; } public DbSet Chapter { get; set; } public DbSet Volume { get; set; } public DbSet AppUser { get; set; } public DbSet MangaFile { get; set; } public DbSet AppUserProgresses { get; set; } public DbSet AppUserRating { get; set; } public DbSet ServerSetting { get; set; } public DbSet AppUserPreferences { get; set; } public DbSet SeriesMetadata { get; set; } public DbSet CollectionTag { get; set; } public DbSet AppUserBookmark { get; set; } public DbSet ReadingList { get; set; } public DbSet ReadingListItem { get; set; } public DbSet Person { get; set; } public DbSet Genre { get; set; } public DbSet Tag { get; set; } public DbSet SiteTheme { get; set; } public DbSet SeriesRelation { get; set; } public DbSet FolderPath { get; set; } public DbSet Device { get; set; } public DbSet ServerStatistics { get; set; } protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); builder.Entity() .HasMany(ur => ur.UserRoles) .WithOne(u => u.User) .HasForeignKey(ur => ur.UserId) .IsRequired(); builder.Entity() .HasMany(ur => ur.UserRoles) .WithOne(u => u.Role) .HasForeignKey(ur => ur.RoleId) .IsRequired(); builder.Entity() .HasOne(pt => pt.Series) .WithMany(p => p.Relations) .HasForeignKey(pt => pt.SeriesId) .OnDelete(DeleteBehavior.Cascade); builder.Entity() .HasOne(pt => pt.TargetSeries) .WithMany(t => t.RelationOf) .HasForeignKey(pt => pt.TargetSeriesId) .OnDelete(DeleteBehavior.Cascade); builder.Entity() .Property(b => b.BookThemeName) .HasDefaultValue("Dark"); builder.Entity() .Property(b => b.BackgroundColor) .HasDefaultValue("#000000"); builder.Entity() .Property(b => b.GlobalPageLayoutMode) .HasDefaultValue(PageLayoutMode.Cards); builder.Entity() .Property(b => b.FolderWatching) .HasDefaultValue(true); builder.Entity() .Property(b => b.IncludeInDashboard) .HasDefaultValue(true); builder.Entity() .Property(b => b.IncludeInRecommended) .HasDefaultValue(true); builder.Entity() .Property(b => b.IncludeInSearch) .HasDefaultValue(true); builder.Entity() .Property(b => b.ManageCollections) .HasDefaultValue(true); } private static void OnEntityTracked(object sender, EntityTrackedEventArgs e) { if (e.FromQuery || e.Entry.State != EntityState.Added || e.Entry.Entity is not IEntityDate entity) return; entity.Created = DateTime.Now; entity.LastModified = DateTime.Now; entity.CreatedUtc = DateTime.UtcNow; entity.LastModifiedUtc = DateTime.UtcNow; } private static void OnEntityStateChanged(object sender, EntityStateChangedEventArgs e) { if (e.NewState != EntityState.Modified || e.Entry.Entity is not IEntityDate entity) return; entity.LastModified = DateTime.Now; entity.LastModifiedUtc = DateTime.UtcNow; } private void OnSaveChanges() { foreach (var saveEntity in ChangeTracker.Entries() .Where(e => e.State == EntityState.Modified) .Select(entry => entry.Entity) .OfType()) { saveEntity.OnSavingChanges(); } } #region SaveChanges overrides public override int SaveChanges() { this.OnSaveChanges(); return base.SaveChanges(); } public override int SaveChanges(bool acceptAllChangesOnSuccess) { this.OnSaveChanges(); return base.SaveChanges(acceptAllChangesOnSuccess); } public override Task SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken)) { this.OnSaveChanges(); return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); } public override Task SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken)) { this.OnSaveChanges(); return base.SaveChangesAsync(cancellationToken); } #endregion }