using System; using DysonNetwork.Drive.Extensions; using DysonNetwork.Drive.Models; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Npgsql; using Npgsql.EntityFrameworkCore.PostgreSQL; using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure; using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal; namespace DysonNetwork.Drive.Data; public class AppDatabase : DbContext, IDisposable { private readonly IConfiguration _configuration; public AppDatabase(DbContextOptions options, IConfiguration configuration) : base(options) { _configuration = configuration; } public DbSet Files { get; set; } = null!; public DbSet FileReferences { get; set; } = null!; protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { if (!optionsBuilder.IsConfigured) { optionsBuilder.UseNpgsql( _configuration.GetConnectionString("DefaultConnection"), o => o.UseNodaTime() ); } } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // Apply snake_case naming convention for all entities foreach (var entity in modelBuilder.Model.GetEntityTypes()) { // Replace table names entity.SetTableName(entity.GetTableName()?.ToSnakeCase()); // Replace column names foreach (var property in entity.GetProperties()) { property.SetColumnName(property.Name.ToSnakeCase()); } // Replace keys foreach (var key in entity.GetKeys()) { key.SetName(key.GetName()?.ToSnakeCase()); } // Replace foreign keys foreach (var key in entity.GetForeignKeys()) { key.SetConstraintName(key.GetConstraintName()?.ToSnakeCase()); } // Replace indexes foreach (var index in entity.GetIndexes()) { index.SetDatabaseName(index.GetDatabaseName()?.ToSnakeCase()); } } // Configure CloudFile entity modelBuilder.Entity(entity => { entity.HasKey(e => e.Id); entity.HasIndex(e => e.StoragePath).IsUnique(); entity.HasIndex(e => e.ContentHash); entity.HasIndex(e => e.UploadedById); entity.HasIndex(e => e.CreatedAt); entity.Property(e => e.Id).ValueGeneratedOnAdd(); entity.Property(e => e.Name).IsRequired(); entity.Property(e => e.OriginalName).IsRequired(); entity.Property(e => e.MimeType).IsRequired(); entity.Property(e => e.StoragePath).IsRequired(); // Configure JSONB column for ExtendedMetadata entity.Property(e => e.ExtendedMetadata) .HasColumnType("jsonb"); // Configure relationships entity.HasMany(e => e.References) .WithOne(e => e.File) .HasForeignKey(e => e.FileId) .OnDelete(DeleteBehavior.Cascade); }); // Configure CloudFileReference entity modelBuilder.Entity(entity => { entity.HasKey(e => e.Id); entity.HasIndex(e => new { e.ResourceId, e.ResourceType, e.ReferenceType }); entity.HasIndex(e => e.ReferenceId); entity.Property(e => e.Id).ValueGeneratedOnAdd(); entity.Property(e => e.ResourceId).IsRequired(); entity.Property(e => e.ResourceType).IsRequired(); entity.Property(e => e.ReferenceType).IsRequired(); // Configure JSONB column for Metadata entity.Property(e => e.Metadata) .HasColumnType("jsonb"); // Configure relationship with CloudFile entity.HasOne(e => e.File) .WithMany(e => e.References) .HasForeignKey(e => e.FileId) .OnDelete(DeleteBehavior.Cascade); }); } public override void Dispose() { base.Dispose(); GC.SuppressFinalize(this); } }