Pack logic cleanup
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
using System.Text.Json;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SVSim.Database.Common;
|
||||
using SVSim.Database.DataSeeders;
|
||||
@@ -112,6 +115,55 @@ public class SVSimDbContext : DbContext
|
||||
modelBuilder.Entity<PackConfigEntry>().OwnsMany(p => p.Banners);
|
||||
modelBuilder.Entity<Viewer>().OwnsMany(v => v.PackOpenCounts);
|
||||
|
||||
// GameConfiguration.Config: on Postgres use EF Core 8's OwnsOne+ToJson(jsonb column).
|
||||
// On SQLite (tests) ToJson's WriteJson has a known NullReferenceException when owned
|
||||
// collections are present — use a plain TEXT value converter instead so the same
|
||||
// entity shape works for both providers without separate test models.
|
||||
bool isSqlite = Database.ProviderName?.Contains("Sqlite", StringComparison.OrdinalIgnoreCase) == true;
|
||||
if (isSqlite)
|
||||
{
|
||||
// Store as JSON text via a value converter; EF treats Config as a single column.
|
||||
var configConverter = new ValueConverter<GameConfigRoot, string>(
|
||||
model => JsonSerializer.Serialize(model, (JsonSerializerOptions?)null),
|
||||
json => JsonSerializer.Deserialize<GameConfigRoot>(json, (JsonSerializerOptions?)null)
|
||||
?? new GameConfigRoot());
|
||||
|
||||
// Deep-equality comparer: serialize both sides and compare strings so that
|
||||
// mutations to nested properties (e.g. Config.Rotation.TsRotationId = "10015")
|
||||
// are detected by EF's snapshot change tracker and written to the DB on SaveChanges.
|
||||
var configComparer = new ValueComparer<GameConfigRoot>(
|
||||
(a, b) => JsonSerializer.Serialize(a, (JsonSerializerOptions?)null)
|
||||
== JsonSerializer.Serialize(b, (JsonSerializerOptions?)null),
|
||||
v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null).GetHashCode(),
|
||||
v => JsonSerializer.Deserialize<GameConfigRoot>(
|
||||
JsonSerializer.Serialize(v, (JsonSerializerOptions?)null),
|
||||
(JsonSerializerOptions?)null) ?? new GameConfigRoot());
|
||||
|
||||
modelBuilder.Entity<GameConfiguration>()
|
||||
.Property(c => c.Config)
|
||||
.HasColumnName("Config")
|
||||
.HasConversion(configConverter, configComparer);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Production path: jsonb column with full OwnsOne tree so EF can filter/project
|
||||
// into sub-properties at the DB level if needed.
|
||||
modelBuilder.Entity<GameConfiguration>().OwnsOne(c => c.Config, b =>
|
||||
{
|
||||
b.ToJson("Config");
|
||||
b.OwnsOne(r => r.DefaultGrants);
|
||||
b.OwnsOne(r => r.Player);
|
||||
b.OwnsOne(r => r.DefaultLoadout);
|
||||
b.OwnsOne(r => r.Challenge);
|
||||
b.OwnsOne(r => r.Rotation);
|
||||
b.OwnsOne(r => r.PackRates, pr =>
|
||||
{
|
||||
pr.OwnsOne(p => p.Default);
|
||||
pr.OwnsMany(p => p.PerSlot);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
new BaseDataSeeder().Seed(modelBuilder);
|
||||
new DefaultSettingsSeeder().Seed(modelBuilder);
|
||||
|
||||
@@ -135,4 +187,22 @@ public class SVSimDbContext : DbContext
|
||||
Database.Migrate();
|
||||
_logger.LogInformation("Migrations applied.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Idempotent runtime seed for entities that can't use HasData (notably GameConfiguration
|
||||
/// because of EF Core 8's HasData+OwnsOne(ToJson) jsonb limitation).
|
||||
/// </summary>
|
||||
public async Task EnsureSeedDataAsync()
|
||||
{
|
||||
if (!await GameConfigurations.AnyAsync(c => c.Id == "default"))
|
||||
{
|
||||
GameConfigurations.Add(new Models.GameConfiguration
|
||||
{
|
||||
Id = "default",
|
||||
Config = new Models.GameConfigRoot(),
|
||||
});
|
||||
await SaveChangesAsync();
|
||||
_logger.LogInformation("Seeded default GameConfiguration row.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user