using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage; using Ping.Domain.Entities; using Ping.Infrastructure.Configuration; using AppIDatabase = Ping.Application.Contracts.IDatabase; namespace Ping.Infrastructure.Persistence { public class Database(DatabaseConfiguration configuration) : DbContext, AppIDatabase { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseNpgsql(configuration.ConnectionString); } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity() .HasKey(m => new { m.ServerId, m.UserId }); modelBuilder.Entity() .HasOne(m => m.Server) .WithMany(s => s.Members) .HasForeignKey(m => m.ServerId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() .HasOne(m => m.User) .WithMany(u => u.ServerMemberships) .HasForeignKey(m => m.UserId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() .HasOne(s => s.Owner) .WithMany() .HasForeignKey(s => s.OwnerId) .OnDelete(DeleteBehavior.Restrict); modelBuilder.Entity() .HasOne(c => c.ServerNavigation) .WithMany(s => s.Channels) .HasForeignKey(c => c.ServerId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() .HasKey(m => new { m.ChannelId, m.UserId }); modelBuilder.Entity() .HasOne(m => m.Channel) .WithMany(c => c.Members) .HasForeignKey(m => m.ChannelId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() .HasOne(m => m.User) .WithMany() .HasForeignKey(m => m.UserId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() .HasOne(m => m.Channel) .WithMany() .HasForeignKey(m => m.ChannelId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() .HasOne(m => m.Author) .WithMany() .HasForeignKey(m => m.AuthorId) .OnDelete(DeleteBehavior.Restrict); modelBuilder.Entity() .HasIndex(m => new { m.ChannelId, m.CreatedAt }); modelBuilder.Entity() .HasOne(a => a.Message) .WithMany(m => m.Attachments) .HasForeignKey(a => a.MessageId) .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() .HasOne(f => f.Requester) .WithMany() .HasForeignKey(f => f.RequesterId) .OnDelete(DeleteBehavior.Restrict); modelBuilder.Entity() .HasOne(f => f.Addressee) .WithMany() .HasForeignKey(f => f.AddresseeId) .OnDelete(DeleteBehavior.Restrict); modelBuilder.Entity() .HasIndex(f => new { f.RequesterId, f.AddresseeId }) .IsUnique(); } public DbSet Users => Set(); public DbSet Friendships => Set(); public DbSet Servers => Set(); public DbSet ServerMembers => Set(); public DbSet Channels => Set(); public DbSet ChannelMembers => Set(); public DbSet Messages => Set(); public DbSet Attachments => Set(); public async Task BeginTransactionAsync( System.Data.IsolationLevel isolationLevel, CancellationToken cancellationToken) { var transaction = await Database.BeginTransactionAsync(isolationLevel, cancellationToken); return new DatabaseTransaction(transaction); } private sealed class DatabaseTransaction(IDbContextTransaction inner) : Ping.Application.Contracts.IDatabaseTransaction { public Task CommitAsync(CancellationToken cancellationToken) => inner.CommitAsync(cancellationToken); public ValueTask DisposeAsync() => inner.DisposeAsync(); } } }