using System; using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using timetracker.Shared; namespace timetracker.Data; public class TimetrackerDbContext : DbContext { private readonly IServiceProvider _serviceProvider; private ITenantProvider? _tenantProvider; private ITenantProvider TenantProvider => _tenantProvider ??= _serviceProvider.GetRequiredService(); public TimetrackerDbContext( DbContextOptions options, IServiceProvider serviceProvider) : base(options) { _serviceProvider = serviceProvider; } public DbSet Tenants => Set(); public DbSet Users => Set(); public DbSet WorkDays => Set(); public DbSet BreakEntries => Set(); public DbSet AppSettings => Set(); public DbSet VacationDays => Set(); public DbSet PublicHolidays => Set(); protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // Subdomain index unique modelBuilder.Entity() .HasIndex(t => t.Subdomain) .IsUnique(); // Global Query Filters (scopes database views by resolved TenantId) modelBuilder.Entity().HasQueryFilter(u => u.TenantId == TenantProvider.TenantId); modelBuilder.Entity().HasQueryFilter(w => w.TenantId == TenantProvider.TenantId); modelBuilder.Entity().HasQueryFilter(v => v.TenantId == TenantProvider.TenantId); modelBuilder.Entity().HasQueryFilter(s => s.TenantId == TenantProvider.TenantId); } public override Task SaveChangesAsync(CancellationToken cancellationToken = default) { var tenantId = TenantProvider.TenantId; if (tenantId.HasValue) { foreach (var entry in ChangeTracker.Entries()) { if (entry.State == EntityState.Added) { var tenantIdProp = entry.Entity.GetType().GetProperty("TenantId"); if (tenantIdProp != null && tenantIdProp.CanWrite) { var currentValue = tenantIdProp.GetValue(entry.Entity); if (currentValue == null || (currentValue is int val && val == 0)) { tenantIdProp.SetValue(entry.Entity, tenantId.Value); } } } } } return base.SaveChangesAsync(cancellationToken); } }