using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Knot.Contracts.Conversations.Application.Abstractions; using Knot.Contracts.Conversations.Domain; using Knot.Contracts.Conversations.Infrastructure.Persistence; using Knot.Shared.Kernel; using Knot.Shared.Kernel.Security; using MediatR; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Diagnostics; using DomainChat = Knot.Contracts.Conversations.Domain.Chat; namespace Knot.Modules.Conversations.Infrastructure.Persistence; public sealed class ChatsDbContext : DbContext, Knot.Contracts.Conversations.Application.Abstractions.IChatsUnitOfWork, Knot.Contracts.Conversations.Infrastructure.Persistence.IChatsDbContext { private readonly IMediator? _mediator; private readonly IEncryptionService? _encryptionService; public ChatsDbContext(DbContextOptions options) : base(options) { } public ChatsDbContext(DbContextOptions options, IMediator mediator, IEncryptionService encryptionService) : this(options) { _mediator = mediator; _encryptionService = encryptionService; } public DbSet Chats => Set(); public DbSet Folders => Set(); public DbSet UserChatSettings => Set(); IQueryable Knot.Contracts.Conversations.Infrastructure.Persistence.IChatsDbContext.Chats => Set().Select(c => new Knot.Contracts.Conversations.Infrastructure.Persistence.Chat { Id = c.Id, Avatar = c.Avatar }); protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { base.OnConfiguring(optionsBuilder); optionsBuilder.ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning)); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.HasDefaultSchema("chats"); modelBuilder.Entity(builder => { builder.ToTable("Chats"); builder.HasKey(c => c.Id); builder.Property(c => c.IsImporting); builder.Property(c => c.ImportJobId); builder.Property(c => c.Type).HasConversion(); builder.OwnsMany(c => c.Members, mb => { mb.ToTable("ChatMembers"); mb.HasKey(m => m.Id); mb.WithOwner().HasForeignKey(m => m.ChatId); mb.HasIndex(m => new { m.ChatId, m.UserId }).IsUnique(); }).Navigation(c => c.Members).UsePropertyAccessMode(PropertyAccessMode.Field); }); modelBuilder.Entity(builder => { builder.ToTable("Folders"); builder.HasKey(f => f.Id); builder.Property(f => f.Type).HasConversion(); }); modelBuilder.Entity(builder => { builder.ToTable("UserChatSettings"); builder.HasKey(s => s.Id); builder.HasIndex(s => new { s.UserId, s.ChatId }).IsUnique(); builder.Property(s => s.FolderIds) .HasConversion( v => string.Join(',', v), v => v.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(Guid.Parse).ToList() ); }); } public override async Task SaveChangesAsync(CancellationToken cancellationToken = default) { var domainEvents = ChangeTracker .Entries() .SelectMany(x => { if (x.Entity is AggregateRoot root) { var events = root.GetDomainEvents().ToList(); root.ClearDomainEvents(); return events; } return Enumerable.Empty(); }) .ToList(); int result = await base.SaveChangesAsync(cancellationToken); if (_mediator != null) { foreach (var domainEvent in domainEvents) { await _mediator.Publish(domainEvent, cancellationToken); } } return result; } }