190 lines
No EOL
6.7 KiB
C#
190 lines
No EOL
6.7 KiB
C#
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);
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enables WAL mode for concurrent reads during background writes.
|
|
/// Should be called once after the database is created/migrated.
|
|
/// </summary>
|
|
public async Task EnableWalModeAsync()
|
|
{
|
|
await Database.ExecuteSqlRawAsync("PRAGMA journal_mode=WAL;");
|
|
}
|
|
|
|
protected override void OnModelCreating(ModelBuilder builder)
|
|
{
|
|
base.OnModelCreating(builder);
|
|
|
|
builder.Entity<Genre>()
|
|
.HasMany(g => g.Games)
|
|
.WithMany(g => g.Genres);
|
|
|
|
builder.Entity<Category>()
|
|
.HasMany(c => c.Games)
|
|
.WithMany(g => g.Categories);
|
|
|
|
builder.Entity<Category>()
|
|
.HasMany(c => c.Children)
|
|
.WithOne(c => c.Parent)
|
|
.IsRequired(false);
|
|
|
|
builder.Entity<Tag>()
|
|
.HasMany(t => t.Games)
|
|
.WithMany(g => g.Tags);
|
|
|
|
builder.Entity<Platform>()
|
|
.HasMany(p => p.Games)
|
|
.WithMany(g => g.Platforms);
|
|
|
|
builder.Entity<Library>()
|
|
.HasOne(l => l.User)
|
|
.WithOne(u => u.Library)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<Library>()
|
|
.HasMany(l => l.Games)
|
|
.WithMany(l => l.Libraries)
|
|
.UsingEntity<Dictionary<string, object>>(
|
|
"LibraryGame",
|
|
lg => lg.HasOne<Game>().WithMany().HasForeignKey("GameId").OnDelete(DeleteBehavior.Cascade),
|
|
lg => lg.HasOne<Library>().WithMany().HasForeignKey("LibraryId").OnDelete(DeleteBehavior.Cascade)
|
|
);
|
|
|
|
builder.Entity<User>()
|
|
.HasOne(u => u.Avatar)
|
|
.WithOne(a => a.User)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
#region Game Relationships
|
|
builder.Entity<Game>()
|
|
.Property(g => g.BaseGameId)
|
|
.HasConversion(new GuidToNullConverter());
|
|
|
|
builder.Entity<Game>()
|
|
.HasMany(g => g.MultiplayerModes)
|
|
.WithOne(m => m.Game)
|
|
.IsRequired(false)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<Game>()
|
|
.HasMany(g => g.Media)
|
|
.WithOne(m => m.Game)
|
|
.IsRequired(false)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<Game>()
|
|
.HasMany(g => g.Developers)
|
|
.WithMany(c => c.DevelopedGames)
|
|
.UsingEntity<Dictionary<string, object>>(
|
|
"GameDeveloper",
|
|
g => g.HasOne<Company>().WithMany().HasForeignKey("DeveloperId"),
|
|
g => g.HasOne<Game>().WithMany().HasForeignKey("GameId")
|
|
);
|
|
|
|
builder.Entity<Game>()
|
|
.HasMany(g => g.Publishers)
|
|
.WithMany(c => c.PublishedGames)
|
|
.UsingEntity<Dictionary<string, object>>(
|
|
"GamePublisher",
|
|
g => g.HasOne<Company>().WithMany().HasForeignKey("PublisherId"),
|
|
g => g.HasOne<Game>().WithMany().HasForeignKey("GameId")
|
|
);
|
|
|
|
builder.Entity<Game>()
|
|
.HasMany(g => g.Redistributables)
|
|
.WithMany(r => r.Games)
|
|
.UsingEntity<Dictionary<string, object>>(
|
|
"GameRedistributable",
|
|
gr => gr.HasOne<Redistributable>().WithMany().HasForeignKey("RedistributableId"),
|
|
gr => gr.HasOne<Game>().WithMany().HasForeignKey("GameId")
|
|
);
|
|
|
|
builder.Entity<Game>()
|
|
.HasMany(g => g.DependentGames)
|
|
.WithOne(g => g.BaseGame)
|
|
.IsRequired(false)
|
|
.OnDelete(DeleteBehavior.SetNull);
|
|
|
|
builder.Entity<Game>()
|
|
.HasOne(g => g.Engine)
|
|
.WithMany(e => e.Games)
|
|
.IsRequired(false)
|
|
.OnDelete(DeleteBehavior.SetNull);
|
|
|
|
builder.Entity<Game>()
|
|
.HasMany(g => g.ExternalIds)
|
|
.WithOne(e => e.Game)
|
|
.IsRequired(false)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
#endregion
|
|
|
|
#region Collection Relationships
|
|
builder.Entity<Collection>()
|
|
.HasMany(c => c.Games)
|
|
.WithMany(g => g.Collections)
|
|
.UsingEntity<Dictionary<string, object>>(
|
|
"CollectionGame",
|
|
cg => cg.HasOne<Game>().WithMany().HasForeignKey("GameId"),
|
|
cg => cg.HasOne<Collection>().WithMany().HasForeignKey("CollectionId")
|
|
);
|
|
#endregion
|
|
}
|
|
|
|
public DbSet<Engine>? Engines { get; set; }
|
|
public DbSet<Game>? Games { get; set; }
|
|
|
|
public DbSet<Genre>? Genres { get; set; }
|
|
|
|
public DbSet<Category>? Categories { get; set; }
|
|
|
|
public DbSet<Tag>? Tags { get; set; }
|
|
|
|
public DbSet<Company>? Companies { get; set; }
|
|
|
|
public DbSet<Redistributable>? Redistributables { get; set; }
|
|
|
|
public DbSet<Media>? Media { get; set; }
|
|
public DbSet<Library>? Libraries { get; set; }
|
|
public DbSet<User>? Users { get; set; }
|
|
}
|
|
} |