using LANCommander.Launcher.Data.Interceptors;
using LANCommander.Launcher.Data.Models;
using LANCommander.SDK;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace LANCommander.Launcher.Data
{
public class DatabaseContext : DbContext
{
private readonly ILoggerFactory LoggerFactory;
public DatabaseContext() { }
public DatabaseContext(ILoggerFactory loggerFactory, DbContextOptions options)
: base(options)
{
LoggerFactory = loggerFactory;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// Skip the SQLite default when the caller has already chosen a provider
// (e.g. tests using EF InMemory) — EF errors out if two providers are registered.
if (optionsBuilder.IsConfigured)
return;
var dbPath = AppPaths.GetConfigPath("LANCommander.db");
var connectionString = new SqliteConnectionStringBuilder
{
DataSource = dbPath,
Cache = SqliteCacheMode.Shared,
}.ToString();
optionsBuilder.AddInterceptors(new AuditingInterceptor());
optionsBuilder.UseLoggerFactory(LoggerFactory);
optionsBuilder.UseSqlite(connectionString, options =>
{
options.CommandTimeout(30);
});
}
///
/// Enables WAL mode for concurrent reads during background writes.
/// Should be called once after the database is created/migrated.
///
public async Task EnableWalModeAsync()
{
await Database.ExecuteSqlRawAsync("PRAGMA journal_mode=WAL;");
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity()
.HasMany(g => g.Games)
.WithMany(g => g.Genres);
builder.Entity()
.HasMany(c => c.Games)
.WithMany(g => g.Categories);
builder.Entity()
.HasMany(c => c.Children)
.WithOne(c => c.Parent)
.IsRequired(false);
builder.Entity()
.HasMany(t => t.Games)
.WithMany(g => g.Tags);
builder.Entity()
.HasMany(p => p.Games)
.WithMany(g => g.Platforms);
builder.Entity()
.HasOne(l => l.User)
.WithOne(u => u.Library)
.OnDelete(DeleteBehavior.Cascade);
builder.Entity()
.HasMany(l => l.Games)
.WithMany(l => l.Libraries)
.UsingEntity>(
"LibraryGame",
lg => lg.HasOne().WithMany().HasForeignKey("GameId").OnDelete(DeleteBehavior.Cascade),
lg => lg.HasOne().WithMany().HasForeignKey("LibraryId").OnDelete(DeleteBehavior.Cascade)
);
builder.Entity()
.HasOne(u => u.Avatar)
.WithOne(a => a.User)
.OnDelete(DeleteBehavior.Cascade);
#region Game Relationships
builder.Entity()
.Property(g => g.BaseGameId)
.HasConversion(new GuidToNullConverter());
builder.Entity()
.HasMany(g => g.MultiplayerModes)
.WithOne(m => m.Game)
.IsRequired(false)
.OnDelete(DeleteBehavior.Cascade);
builder.Entity()
.HasMany(g => g.Media)
.WithOne(m => m.Game)
.IsRequired(false)
.OnDelete(DeleteBehavior.Cascade);
builder.Entity()
.HasMany(g => g.Developers)
.WithMany(c => c.DevelopedGames)
.UsingEntity>(
"GameDeveloper",
g => g.HasOne().WithMany().HasForeignKey("DeveloperId"),
g => g.HasOne().WithMany().HasForeignKey("GameId")
);
builder.Entity()
.HasMany(g => g.Publishers)
.WithMany(c => c.PublishedGames)
.UsingEntity>(
"GamePublisher",
g => g.HasOne().WithMany().HasForeignKey("PublisherId"),
g => g.HasOne().WithMany().HasForeignKey("GameId")
);
builder.Entity()
.HasMany(g => g.Redistributables)
.WithMany(r => r.Games)
.UsingEntity>(
"GameRedistributable",
gr => gr.HasOne().WithMany().HasForeignKey("RedistributableId"),
gr => gr.HasOne().WithMany().HasForeignKey("GameId")
);
builder.Entity()
.HasMany(g => g.DependentGames)
.WithOne(g => g.BaseGame)
.IsRequired(false)
.OnDelete(DeleteBehavior.SetNull);
builder.Entity()
.HasOne(g => g.Engine)
.WithMany(e => e.Games)
.IsRequired(false)
.OnDelete(DeleteBehavior.SetNull);
builder.Entity()
.HasMany(g => g.ExternalIds)
.WithOne(e => e.Game)
.IsRequired(false)
.OnDelete(DeleteBehavior.Cascade);
#endregion
#region Collection Relationships
builder.Entity()
.HasMany(c => c.Games)
.WithMany(g => g.Collections)
.UsingEntity>(
"CollectionGame",
cg => cg.HasOne().WithMany().HasForeignKey("GameId"),
cg => cg.HasOne().WithMany().HasForeignKey("CollectionId")
);
#endregion
}
public DbSet? Engines { get; set; }
public DbSet? Games { get; set; }
public DbSet? Genres { get; set; }
public DbSet? Categories { get; set; }
public DbSet? Tags { get; set; }
public DbSet? Companies { get; set; }
public DbSet? Redistributables { get; set; }
public DbSet? Media { get; set; }
public DbSet? Libraries { get; set; }
public DbSet? Users { get; set; }
}
}