using System.Data; using System.IO; using Microsoft.EntityFrameworkCore; using UtopiaCanteenSystem.Models; namespace UtopiaCanteenSystem.Data; /// /// SQLite DbContext for Labour and ScanRecord tables. /// Database file is created in application directory on first run. /// public class AppDbContext : DbContext { private static readonly string DbPath = Path.Combine( AppDomain.CurrentDomain.BaseDirectory, "utopia_canteen.db"); public DbSet Labour { get; set; } public DbSet ScanRecords { get; set; } public DbSet AdminLoginRecords { get; set; } public AppDbContext() { } public AppDbContext(DbContextOptions options) : base(options) { } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { if (!optionsBuilder.IsConfigured) optionsBuilder.UseSqlite($"Data Source={DbPath}"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { // Labour: CardId should be indexed for lookups. modelBuilder.Entity(e => { e.HasKey(x => x.Id); e.HasIndex(x => x.CardId); }); // ScanRecord: Index for unsynced queries and by ScanTime. modelBuilder.Entity(e => { e.HasKey(x => x.Id); e.HasIndex(x => x.IsSynced); e.HasIndex(x => x.ScanTime); }); modelBuilder.Entity(e => { e.HasKey(x => x.Id); e.HasIndex(x => x.LoginTimeUtc); }); } /// /// Ensures database exists and is migrated. Call on app startup. /// public void EnsureDatabaseCreated() { Database.EnsureCreated(); UpgradeScanRecordsSchemaIfNeeded(); EnsureAdminLoginTableExists(); } /// /// Lightweight schema upgrade: add SiteId and DeviceId to ScanRecords if missing (no EF migrations). /// Does not delete any data. /// private void UpgradeScanRecordsSchemaIfNeeded() { try { var conn = Database.GetDbConnection(); if (conn.State != ConnectionState.Open) conn.Open(); var columns = new List(); using (var cmd = conn.CreateCommand()) { cmd.CommandText = "SELECT name FROM pragma_table_info('ScanRecords')"; using var r = cmd.ExecuteReader(); while (r.Read()) columns.Add(r.GetString(0)); } if (!columns.Contains("SiteId", StringComparer.OrdinalIgnoreCase)) { using var cmd = conn.CreateCommand(); cmd.CommandText = "ALTER TABLE ScanRecords ADD COLUMN SiteId TEXT DEFAULT ''"; cmd.ExecuteNonQuery(); } if (!columns.Contains("DeviceId", StringComparer.OrdinalIgnoreCase)) { using var cmd = conn.CreateCommand(); cmd.CommandText = "ALTER TABLE ScanRecords ADD COLUMN DeviceId TEXT DEFAULT ''"; cmd.ExecuteNonQuery(); } } catch { // Ignore; existing DB may already have columns or be incompatible } } /// /// Lightweight creation of AdminLoginRecords table for existing databases. /// private void EnsureAdminLoginTableExists() { try { var conn = Database.GetDbConnection(); if (conn.State != ConnectionState.Open) conn.Open(); using var cmd = conn.CreateCommand(); cmd.CommandText = "CREATE TABLE IF NOT EXISTS AdminLoginRecords (" + "Id INTEGER PRIMARY KEY AUTOINCREMENT, " + "Username TEXT NOT NULL, " + "EmployeeId TEXT NOT NULL, " + "LoginTimeUtc TEXT NOT NULL)"; cmd.ExecuteNonQuery(); } catch { // Ignore; table may already exist or DB may be read-only. } } }