Utopia-Canteen-System/Data/AppDbContext.cs

133 lines
4.1 KiB
C#

using System.Data;
using System.IO;
using Microsoft.EntityFrameworkCore;
using UtopiaCanteenSystem.Models;
namespace UtopiaCanteenSystem.Data;
/// <summary>
/// SQLite DbContext for Labour and ScanRecord tables.
/// Database file is created in application directory on first run.
/// </summary>
public class AppDbContext : DbContext
{
private static readonly string DbPath = Path.Combine(
AppDomain.CurrentDomain.BaseDirectory,
"utopia_canteen.db");
public DbSet<Labour> Labour { get; set; }
public DbSet<ScanRecord> ScanRecords { get; set; }
public DbSet<AdminLoginRecord> AdminLoginRecords { get; set; }
public AppDbContext() { }
public AppDbContext(DbContextOptions<AppDbContext> 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<Labour>(e =>
{
e.HasKey(x => x.Id);
e.HasIndex(x => x.CardId);
});
// ScanRecord: Index for unsynced queries and by ScanTime.
modelBuilder.Entity<ScanRecord>(e =>
{
e.HasKey(x => x.Id);
e.HasIndex(x => x.IsSynced);
e.HasIndex(x => x.ScanTime);
});
modelBuilder.Entity<AdminLoginRecord>(e =>
{
e.HasKey(x => x.Id);
e.HasIndex(x => x.LoginTimeUtc);
});
}
/// <summary>
/// Ensures database exists and is migrated. Call on app startup.
/// </summary>
public void EnsureDatabaseCreated()
{
Database.EnsureCreated();
UpgradeScanRecordsSchemaIfNeeded();
EnsureAdminLoginTableExists();
}
/// <summary>
/// Lightweight schema upgrade: add SiteId and DeviceId to ScanRecords if missing (no EF migrations).
/// Does not delete any data.
/// </summary>
private void UpgradeScanRecordsSchemaIfNeeded()
{
try
{
var conn = Database.GetDbConnection();
if (conn.State != ConnectionState.Open)
conn.Open();
var columns = new List<string>();
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
}
}
/// <summary>
/// Lightweight creation of AdminLoginRecords table for existing databases.
/// </summary>
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.
}
}
}