using Indotalent.ConfigBackEnd.Extensions; using Indotalent.ConfigBackEnd.Interfaces; using Indotalent.Data.Abstracts; using Indotalent.Data.Entities; using Indotalent.Data.Interfaces; using Indotalent.Infrastructure.AutoNumberGenerator; using Indotalent.Shared.Consts; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; namespace Indotalent.Infrastructure.Database; public class AppDbContext : IdentityDbContext { private readonly IServiceScopeFactory _scopeFactory; private readonly ICurrentUserService _currentUserService; public AppDbContext( DbContextOptions options, IServiceScopeFactory scopeFactory, ICurrentUserService currentUserService) : base(options) { _scopeFactory = scopeFactory; _currentUserService = currentUserService; } public DbSet AutoNumberSequence { get; set; } = default!; public DbSet Currency { get; set; } = default!; public DbSet Company { get; set; } = default!; public DbSet Tax { get; set; } = default!; public DbSet PaymentMethod { get; set; } = default!; public DbSet Booking { get; set; } = default!; public DbSet BookingGroup { get; set; } = default!; public DbSet BookingResource { get; set; } = default!; public DbSet Bill { get; set; } = default!; public DbSet PaymentDisburse { get; set; } = default!; public DbSet Invoice { get; set; } = default!; public DbSet PaymentReceive { get; set; } = default!; public DbSet ProductGroup { get; set; } = default!; public DbSet PurchaseOrder { get; set; } = default!; public DbSet PurchaseOrderItem { get; set; } = default!; public DbSet Patient { get; set; } = default!; public DbSet PatientCategory { get; set; } = default!; public DbSet PatientContact { get; set; } = default!; public DbSet PatientGroup { get; set; } = default!; public DbSet Product { get; set; } = default!; public DbSet SalesOrderGroup { get; set; } = default!; public DbSet SalesOrderCategory { get; set; } = default!; public DbSet SalesOrder { get; set; } = default!; public DbSet SalesOrderItem { get; set; } = default!; public DbSet Todo { get; set; } = default!; public DbSet TodoItem { get; set; } = default!; public DbSet UnitMeasure { get; set; } = default!; public DbSet Vendor { get; set; } = default!; public DbSet VendorCategory { get; set; } = default!; public DbSet VendorContact { get; set; } = default!; public DbSet VendorGroup { get; set; } = default!; public DbSet Warehouse { get; set; } = default!; public DbSet InventoryTransaction { get; set; } = default!; public DbSet Employee { get; set; } = default!; public DbSet EmployeeCategory { get; set; } = default!; public DbSet EmployeeGroup { get; set; } = default!; public DbSet MedicalRecordCategory { get; set; } = default!; public DbSet MedicalRecordGroup { get; set; } = default!; public DbSet MedicalRecord { get; set; } = default!; public override int SaveChanges() { ApplySoftDelete(); ApplyAudit(_currentUserService.UserId ?? string.Empty); return base.SaveChanges(); } public override async Task SaveChangesAsync(CancellationToken cancellationToken = default) { ChangeTracker.DetectChanges(); ApplySoftDelete(); ApplyAudit(_currentUserService.UserId ?? string.Empty); await ApplyAutoNumber(cancellationToken); return await base.SaveChangesAsync(cancellationToken); } private void ApplySoftDelete() { var entries = ChangeTracker.Entries() .Where(e => e.State == EntityState.Deleted); foreach (var entry in entries) { entry.State = EntityState.Modified; entry.Entity.IsDeleted = true; } } private void ApplyAudit(string userId) { var entries = ChangeTracker.Entries() .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified) .ToList(); foreach (var entry in entries) { var now = DateTimeOffset.UtcNow; var auditEntity = entry.Entity; if (entry.State == EntityState.Added) { auditEntity.CreatedAt = now; auditEntity.CreatedBy = userId; } else { entry.Property(nameof(IHasAudit.CreatedAt)).IsModified = false; entry.Property(nameof(IHasAudit.CreatedBy)).IsModified = false; } auditEntity.UpdatedAt = now; auditEntity.UpdatedBy = userId; } } private async Task ApplyAutoNumber(CancellationToken ct) { var entries = ChangeTracker .Entries() .Where(e => e.State == EntityState.Added && string.IsNullOrEmpty(e.Entity.AutoNumber)) .ToList(); if (!entries.Any()) return; using var scope = _scopeFactory.CreateScope(); var generator = scope.ServiceProvider.GetRequiredService(); foreach (var entry in entries) { var entityName = entry.Entity.GetType().Name; var shortName = entityName.ToShortNameConsonant(3); entry.Entity.AutoNumber = await generator.GenerateNextNumberAsync( entityName: entityName, prefixTemplate: $"{shortName}/{{Year}}/", paddingLength: 4, cancellationToken: ct ); } } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); if (Database.ProviderName?.Contains("MySql") == true) { foreach (var entity in modelBuilder.Model.GetEntityTypes()) { if (entity.GetTableName()!.StartsWith("AspNet")) { foreach (var property in entity.GetProperties().Where(p => p.ClrType == typeof(string))) { property.SetMaxLength(191); } } } } foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { var type = entityType.ClrType; if (typeof(BaseEntity).IsAssignableFrom(type)) { modelBuilder.Entity(type) .Property("Id") .HasMaxLength(GlobalConsts.StringLengthId) .IsFixedLength(); } if (typeof(IHasAudit).IsAssignableFrom(type)) { modelBuilder.Entity(type) .Property(nameof(IHasAudit.CreatedBy)) .HasMaxLength(GlobalConsts.StringLengthShort); modelBuilder.Entity(type) .Property(nameof(IHasAudit.UpdatedBy)) .HasMaxLength(GlobalConsts.StringLengthShort); } if (typeof(IHasIsDeleted).IsAssignableFrom(type)) { modelBuilder.Entity(type).HasQueryFilter(GetNotDeletedOnlyFilter(type)); } } modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly); } private static System.Linq.Expressions.LambdaExpression GetNotDeletedOnlyFilter(Type type) { var parameter = System.Linq.Expressions.Expression.Parameter(type, "e"); var property = System.Linq.Expressions.Expression.Property(parameter, nameof(IHasIsDeleted.IsDeleted)); var falseConstant = System.Linq.Expressions.Expression.Constant(false); var comparison = System.Linq.Expressions.Expression.Equal(property, falseConstant); return System.Linq.Expressions.Expression.Lambda(comparison, parameter); } }