More features
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
using System.Globalization;
|
||||
using CsvHelper;
|
||||
using CsvHelper.Configuration;
|
||||
using DCGEngine.Database.Interfaces;
|
||||
using SVSim.Database.Common;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SVSim.Database.Models;
|
||||
|
||||
@@ -12,6 +12,17 @@ namespace SVSim.Database.DataSeeders;
|
||||
/// </summary>
|
||||
public class BaseDataSeeder : IDataSeeder
|
||||
{
|
||||
private static string DataPath(string fileName) =>
|
||||
Path.Combine(AppContext.BaseDirectory, "Data", fileName);
|
||||
|
||||
private static List<T> ReadCsv<T, TMap>(string fileName) where TMap : ClassMap<T>, new()
|
||||
{
|
||||
using StreamReader reader = new(DataPath(fileName));
|
||||
using CsvReader csv = new(reader, CultureInfo.InvariantCulture);
|
||||
csv.Context.RegisterClassMap<TMap>();
|
||||
return csv.GetRecords<T>().ToList();
|
||||
}
|
||||
|
||||
private class ClassEntryMap : ClassMap<ClassEntry>
|
||||
{
|
||||
public ClassEntryMap()
|
||||
@@ -108,99 +119,33 @@ public class BaseDataSeeder : IDataSeeder
|
||||
|
||||
public void Seed(ModelBuilder builder)
|
||||
{
|
||||
List<ClassEntry> classes = new();
|
||||
List<LeaderSkinEntry> leaderSkins = new();
|
||||
List<EmblemEntry> emblems = new();
|
||||
List<DegreeEntry> degrees = new();
|
||||
List<SleeveEntry> sleeves = new();
|
||||
List<BattlefieldEntry> battlefields = new();
|
||||
List<MyPageBackgroundEntry> myPageBackgrounds = new();
|
||||
List<ClassExpEntry> classexp = new();
|
||||
List<RankInfoEntry> rankinfos = new();
|
||||
|
||||
using (StreamReader reader = new("data/classes.csv"))
|
||||
// Migrations bake the HasData rows into InsertData calls — once the migration is
|
||||
// generated, runtime model-creation no longer needs the CSVs. Tools that only query
|
||||
// an already-migrated DB (e.g. SVSim.CardImport) don't ship the Data folder; skip
|
||||
// gracefully so DbContext construction succeeds for them.
|
||||
if (!File.Exists(DataPath("classes.csv")))
|
||||
{
|
||||
using CsvReader csv = new(reader, CultureInfo.InvariantCulture);
|
||||
csv.Context.RegisterClassMap<ClassEntryMap>();
|
||||
|
||||
IEnumerable<ClassEntry> records = csv.GetRecords<ClassEntry>();
|
||||
classes.AddRange(records);
|
||||
}
|
||||
|
||||
using (StreamReader reader = new("data/leaderskins.csv"))
|
||||
{
|
||||
using CsvReader csv = new(reader, CultureInfo.InvariantCulture);
|
||||
csv.Context.RegisterClassMap<LeaderSkinEntryMap>();
|
||||
|
||||
IEnumerable<LeaderSkinEntry> records = csv.GetRecords<LeaderSkinEntry>();
|
||||
leaderSkins.AddRange(records);
|
||||
|
||||
leaderSkins.ForEach(skin =>
|
||||
{
|
||||
if (skin.ClassId == 0)
|
||||
{
|
||||
skin.ClassId = null;
|
||||
}
|
||||
});
|
||||
Console.Error.WriteLine($"[BaseDataSeeder] Skipping seed: Data folder not found at {DataPath("")}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Load rest of default data
|
||||
using (StreamReader reader = new("data/emblems.csv"))
|
||||
List<ClassEntry> classes = ReadCsv<ClassEntry, ClassEntryMap>("classes.csv");
|
||||
List<LeaderSkinEntry> leaderSkins = ReadCsv<LeaderSkinEntry, LeaderSkinEntryMap>("leaderskins.csv");
|
||||
leaderSkins.ForEach(skin =>
|
||||
{
|
||||
using CsvReader csv = new(reader, CultureInfo.InvariantCulture);
|
||||
csv.Context.RegisterClassMap<EmblemEntryMap>();
|
||||
|
||||
IEnumerable<EmblemEntry> records = csv.GetRecords<EmblemEntry>();
|
||||
emblems.AddRange(records);
|
||||
}
|
||||
using (StreamReader reader = new("data/degrees.csv"))
|
||||
{
|
||||
using CsvReader csv = new(reader, CultureInfo.InvariantCulture);
|
||||
csv.Context.RegisterClassMap<DegreeEntryMap>();
|
||||
|
||||
IEnumerable<DegreeEntry> records = csv.GetRecords<DegreeEntry>();
|
||||
degrees.AddRange(records);
|
||||
}
|
||||
using (StreamReader reader = new("data/sleeves.csv"))
|
||||
{
|
||||
using CsvReader csv = new(reader, CultureInfo.InvariantCulture);
|
||||
csv.Context.RegisterClassMap<SleeveEntryMap>();
|
||||
|
||||
IEnumerable<SleeveEntry> records = csv.GetRecords<SleeveEntry>();
|
||||
sleeves.AddRange(records);
|
||||
}
|
||||
using (StreamReader reader = new("data/battlefields.csv"))
|
||||
{
|
||||
using CsvReader csv = new(reader, CultureInfo.InvariantCulture);
|
||||
csv.Context.RegisterClassMap<BattlefieldEntryMap>();
|
||||
|
||||
IEnumerable<BattlefieldEntry> records = csv.GetRecords<BattlefieldEntry>();
|
||||
battlefields.AddRange(records);
|
||||
}
|
||||
using (StreamReader reader = new("data/mypagebackgrounds.csv"))
|
||||
{
|
||||
using CsvReader csv = new(reader, CultureInfo.InvariantCulture);
|
||||
csv.Context.RegisterClassMap<MyPageBackgroundEntryMap>();
|
||||
|
||||
IEnumerable<MyPageBackgroundEntry> records = csv.GetRecords<MyPageBackgroundEntry>();
|
||||
myPageBackgrounds.AddRange(records);
|
||||
}
|
||||
using (StreamReader reader = new("data/ranks.csv"))
|
||||
{
|
||||
using CsvReader csv = new(reader, CultureInfo.InvariantCulture);
|
||||
csv.Context.RegisterClassMap<RankInfoEntryMap>();
|
||||
|
||||
IEnumerable<RankInfoEntry> records = csv.GetRecords<RankInfoEntry>();
|
||||
rankinfos.AddRange(records);
|
||||
}
|
||||
using (StreamReader reader = new("data/classexp.csv"))
|
||||
{
|
||||
using CsvReader csv = new(reader, CultureInfo.InvariantCulture);
|
||||
csv.Context.RegisterClassMap<ClassExpEntryMap>();
|
||||
|
||||
IEnumerable<ClassExpEntry> records = csv.GetRecords<ClassExpEntry>();
|
||||
classexp.AddRange(records);
|
||||
}
|
||||
if (skin.ClassId == 0)
|
||||
{
|
||||
skin.ClassId = null;
|
||||
}
|
||||
});
|
||||
|
||||
List<EmblemEntry> emblems = ReadCsv<EmblemEntry, EmblemEntryMap>("emblems.csv");
|
||||
List<DegreeEntry> degrees = ReadCsv<DegreeEntry, DegreeEntryMap>("degrees.csv");
|
||||
List<SleeveEntry> sleeves = ReadCsv<SleeveEntry, SleeveEntryMap>("sleeves.csv");
|
||||
List<BattlefieldEntry> battlefields = ReadCsv<BattlefieldEntry, BattlefieldEntryMap>("battlefields.csv");
|
||||
List<MyPageBackgroundEntry> myPageBackgrounds = ReadCsv<MyPageBackgroundEntry, MyPageBackgroundEntryMap>("mypagebackgrounds.csv");
|
||||
List<RankInfoEntry> rankinfos = ReadCsv<RankInfoEntry, RankInfoEntryMap>("ranks.csv");
|
||||
List<ClassExpEntry> classexp = ReadCsv<ClassExpEntry, ClassExpEntryMap>("classexp.csv");
|
||||
|
||||
builder.Entity<ClassEntry>().HasData(classes);
|
||||
builder.Entity<LeaderSkinEntry>().HasData(leaderSkins);
|
||||
@@ -212,4 +157,4 @@ public class BaseDataSeeder : IDataSeeder
|
||||
builder.Entity<RankInfoEntry>().HasData(rankinfos);
|
||||
builder.Entity<ClassExpEntry>().HasData(classexp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using DCGEngine.Database.Interfaces;
|
||||
using SVSim.Database.Common;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SVSim.Database.Models;
|
||||
|
||||
|
||||
33897
SVSim.Database/Migrations/20240929140334_Initial.Designer.cs
generated
33897
SVSim.Database/Migrations/20240929140334_Initial.Designer.cs
generated
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -25,78 +25,6 @@ namespace SVSim.Database.Migrations
|
||||
modelBuilder.HasSequence("ShortUdidSequence")
|
||||
.StartsAt(400000000L);
|
||||
|
||||
modelBuilder.Entity("DCGEngine.Database.Models.CardEntry", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int?>("Attack")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("DateCreated")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime?>("DateUpdated")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int?>("Defense")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Discriminator")
|
||||
.IsRequired()
|
||||
.HasMaxLength(21)
|
||||
.HasColumnType("character varying(21)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int?>("PrimaryResourceCost")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int?>("ShadowverseCardSetEntryId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ShadowverseCardSetEntryId");
|
||||
|
||||
b.ToTable("CardEntry");
|
||||
|
||||
b.HasDiscriminator().HasValue("CardEntry");
|
||||
|
||||
b.UseTphMappingStrategy();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DCGEngine.Database.Models.DeckEntry", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("DateCreated")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime?>("DateUpdated")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Discriminator")
|
||||
.IsRequired()
|
||||
.HasMaxLength(21)
|
||||
.HasColumnType("character varying(21)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("DeckEntry");
|
||||
|
||||
b.HasDiscriminator().HasValue("DeckEntry");
|
||||
|
||||
b.UseTphMappingStrategy();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DegreeEntryViewer", b =>
|
||||
{
|
||||
b.Property<int>("DegreesId")
|
||||
@@ -177,7 +105,7 @@ namespace SVSim.Database.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("BattlefieldEntry");
|
||||
b.ToTable("Battlefields");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
@@ -311,7 +239,7 @@ namespace SVSim.Database.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ClassEntry");
|
||||
b.ToTable("Classes");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
@@ -380,7 +308,7 @@ namespace SVSim.Database.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ClassExpEntry");
|
||||
b.ToTable("ClassExpCurve");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
@@ -1298,7 +1226,7 @@ namespace SVSim.Database.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("DegreeEntry");
|
||||
b.ToTable("Degrees");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
@@ -10396,7 +10324,7 @@ namespace SVSim.Database.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("EmblemEntry");
|
||||
b.ToTable("Emblems");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
@@ -21516,7 +21444,7 @@ namespace SVSim.Database.Migrations
|
||||
|
||||
b.HasIndex("DefaultSleeveId");
|
||||
|
||||
b.ToTable("GameConfiguration");
|
||||
b.ToTable("GameConfigurations");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
@@ -21551,7 +21479,7 @@ namespace SVSim.Database.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ItemEntry");
|
||||
b.ToTable("Items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SVSim.Database.Models.LeaderSkinEntry", b =>
|
||||
@@ -21579,7 +21507,7 @@ namespace SVSim.Database.Migrations
|
||||
|
||||
b.HasIndex("ClassId");
|
||||
|
||||
b.ToTable("LeaderSkinEntry");
|
||||
b.ToTable("LeaderSkins");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
@@ -24929,7 +24857,7 @@ namespace SVSim.Database.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("MyPageBackgroundEntry");
|
||||
b.ToTable("MyPageBackgrounds");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
@@ -25081,7 +25009,7 @@ namespace SVSim.Database.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("RankInfoEntry");
|
||||
b.ToTable("RankInfo");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
@@ -25695,6 +25623,48 @@ namespace SVSim.Database.Migrations
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SVSim.Database.Models.ShadowverseCardEntry", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int?>("Attack")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int?>("ClassId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("DateCreated")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime?>("DateUpdated")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int?>("Defense")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int?>("PrimaryResourceCost")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("Rarity")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int?>("ShadowverseCardSetEntryId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClassId");
|
||||
|
||||
b.HasIndex("ShadowverseCardSetEntryId");
|
||||
|
||||
b.ToTable("Cards");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SVSim.Database.Models.ShadowverseCardSetEntry", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -25718,7 +25688,56 @@ namespace SVSim.Database.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ShadowverseCardSetEntry");
|
||||
b.ToTable("CardSets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SVSim.Database.Models.ShadowverseDeckEntry", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("ClassId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("DateCreated")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime?>("DateUpdated")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int>("Format")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("LeaderSkinId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("Number")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<bool>("RandomLeaderSkin")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<int>("SleeveId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long?>("ViewerId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ClassId");
|
||||
|
||||
b.HasIndex("LeaderSkinId");
|
||||
|
||||
b.HasIndex("SleeveId");
|
||||
|
||||
b.HasIndex("ViewerId");
|
||||
|
||||
b.ToTable("Decks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SVSim.Database.Models.SleeveEntry", b =>
|
||||
@@ -25734,7 +25753,7 @@ namespace SVSim.Database.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SleeveEntry");
|
||||
b.ToTable("Sleeves");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
@@ -33281,7 +33300,7 @@ namespace SVSim.Database.Migrations
|
||||
|
||||
b.HasIndex("ShortUdid");
|
||||
|
||||
b.ToTable("Viewer");
|
||||
b.ToTable("Viewers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SleeveEntryViewer", b =>
|
||||
@@ -33299,106 +33318,6 @@ namespace SVSim.Database.Migrations
|
||||
b.ToTable("SleeveEntryViewer");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SVSim.Database.Models.ShadowverseCardEntry", b =>
|
||||
{
|
||||
b.HasBaseType("DCGEngine.Database.Models.CardEntry");
|
||||
|
||||
b.Property<int?>("ClassId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("Rarity")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasIndex("ClassId");
|
||||
|
||||
b.HasDiscriminator().HasValue("ShadowverseCardEntry");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SVSim.Database.Models.ShadowverseDeckEntry", b =>
|
||||
{
|
||||
b.HasBaseType("DCGEngine.Database.Models.DeckEntry");
|
||||
|
||||
b.Property<int>("ClassId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("Format")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("LeaderSkinId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("Number")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<bool>("RandomLeaderSkin")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<int>("SleeveId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long?>("ViewerId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasIndex("ClassId");
|
||||
|
||||
b.HasIndex("LeaderSkinId");
|
||||
|
||||
b.HasIndex("SleeveId");
|
||||
|
||||
b.HasIndex("ViewerId");
|
||||
|
||||
b.HasDiscriminator().HasValue("ShadowverseDeckEntry");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DCGEngine.Database.Models.CardEntry", b =>
|
||||
{
|
||||
b.HasOne("SVSim.Database.Models.ShadowverseCardSetEntry", null)
|
||||
.WithMany("Cards")
|
||||
.HasForeignKey("ShadowverseCardSetEntryId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DCGEngine.Database.Models.DeckEntry", b =>
|
||||
{
|
||||
b.OwnsMany("DCGEngine.Database.Models.DeckCard", "Cards", b1 =>
|
||||
{
|
||||
b1.Property<Guid>("DeckId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b1.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property<int>("Id"));
|
||||
|
||||
b1.Property<long>("CardId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b1.Property<int>("Count")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b1.HasKey("DeckId", "Id");
|
||||
|
||||
b1.HasIndex("CardId");
|
||||
|
||||
b1.ToTable("DeckEntry_Cards");
|
||||
|
||||
b1.HasOne("DCGEngine.Database.Models.CardEntry", "Card")
|
||||
.WithMany()
|
||||
.HasForeignKey("CardId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b1.WithOwner("Deck")
|
||||
.HasForeignKey("DeckId");
|
||||
|
||||
b1.Navigation("Card");
|
||||
|
||||
b1.Navigation("Deck");
|
||||
});
|
||||
|
||||
b.Navigation("Cards");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DegreeEntryViewer", b =>
|
||||
{
|
||||
b.HasOne("SVSim.Database.Models.DegreeEntry", null)
|
||||
@@ -33503,6 +33422,108 @@ namespace SVSim.Database.Migrations
|
||||
b.Navigation("Class");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SVSim.Database.Models.ShadowverseCardEntry", b =>
|
||||
{
|
||||
b.HasOne("SVSim.Database.Models.ClassEntry", "Class")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClassId");
|
||||
|
||||
b.HasOne("SVSim.Database.Models.ShadowverseCardSetEntry", null)
|
||||
.WithMany("Cards")
|
||||
.HasForeignKey("ShadowverseCardSetEntryId");
|
||||
|
||||
b.OwnsOne("SVSim.Database.Models.CardCollectionInfo", "CollectionInfo", b1 =>
|
||||
{
|
||||
b1.Property<long>("ShadowverseCardEntryId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b1.Property<int>("CraftCost")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b1.Property<int>("DustReward")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b1.HasKey("ShadowverseCardEntryId");
|
||||
|
||||
b1.ToTable("Cards");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("ShadowverseCardEntryId");
|
||||
});
|
||||
|
||||
b.Navigation("Class");
|
||||
|
||||
b.Navigation("CollectionInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SVSim.Database.Models.ShadowverseDeckEntry", b =>
|
||||
{
|
||||
b.HasOne("SVSim.Database.Models.ClassEntry", "Class")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClassId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SVSim.Database.Models.LeaderSkinEntry", "LeaderSkin")
|
||||
.WithMany()
|
||||
.HasForeignKey("LeaderSkinId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SVSim.Database.Models.SleeveEntry", "Sleeve")
|
||||
.WithMany()
|
||||
.HasForeignKey("SleeveId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SVSim.Database.Models.Viewer", null)
|
||||
.WithMany("Decks")
|
||||
.HasForeignKey("ViewerId");
|
||||
|
||||
b.OwnsMany("SVSim.Database.Models.DeckCard", "Cards", b1 =>
|
||||
{
|
||||
b1.Property<Guid>("ShadowverseDeckEntryId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b1.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property<int>("Id"));
|
||||
|
||||
b1.Property<long>("CardId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b1.Property<int>("Count")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b1.HasKey("ShadowverseDeckEntryId", "Id");
|
||||
|
||||
b1.HasIndex("CardId");
|
||||
|
||||
b1.ToTable("DeckCard");
|
||||
|
||||
b1.HasOne("SVSim.Database.Models.ShadowverseCardEntry", "Card")
|
||||
.WithMany()
|
||||
.HasForeignKey("CardId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("ShadowverseDeckEntryId");
|
||||
|
||||
b1.Navigation("Card");
|
||||
});
|
||||
|
||||
b.Navigation("Cards");
|
||||
|
||||
b.Navigation("Class");
|
||||
|
||||
b.Navigation("LeaderSkin");
|
||||
|
||||
b.Navigation("Sleeve");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SVSim.Database.Models.Viewer", b =>
|
||||
{
|
||||
b.OwnsMany("SVSim.Database.Models.OwnedCardEntry", "Cards", b1 =>
|
||||
@@ -33531,7 +33552,7 @@ namespace SVSim.Database.Migrations
|
||||
|
||||
b1.ToTable("OwnedCardEntry");
|
||||
|
||||
b1.HasOne("DCGEngine.Database.Models.CardEntry", "Card")
|
||||
b1.HasOne("SVSim.Database.Models.ShadowverseCardEntry", "Card")
|
||||
.WithMany()
|
||||
.HasForeignKey("CardId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@@ -33694,7 +33715,7 @@ namespace SVSim.Database.Migrations
|
||||
|
||||
b1.HasKey("ViewerId");
|
||||
|
||||
b1.ToTable("Viewer");
|
||||
b1.ToTable("Viewers");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("ViewerId");
|
||||
@@ -33733,7 +33754,7 @@ namespace SVSim.Database.Migrations
|
||||
|
||||
b1.HasIndex("SelectedEmblemId");
|
||||
|
||||
b1.ToTable("Viewer");
|
||||
b1.ToTable("Viewers");
|
||||
|
||||
b1.HasOne("SVSim.Database.Models.DegreeEntry", "SelectedDegree")
|
||||
.WithMany()
|
||||
@@ -33774,7 +33795,7 @@ namespace SVSim.Database.Migrations
|
||||
|
||||
b1.HasKey("ViewerId");
|
||||
|
||||
b1.ToTable("Viewer");
|
||||
b1.ToTable("Viewers");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("ViewerId");
|
||||
@@ -33813,67 +33834,6 @@ namespace SVSim.Database.Migrations
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SVSim.Database.Models.ShadowverseCardEntry", b =>
|
||||
{
|
||||
b.HasOne("SVSim.Database.Models.ClassEntry", "Class")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClassId");
|
||||
|
||||
b.OwnsOne("SVSim.Database.Models.CardCollectionInfo", "CollectionInfo", b1 =>
|
||||
{
|
||||
b1.Property<long>("ShadowverseCardEntryId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b1.Property<int>("CraftCost")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b1.Property<int>("DustReward")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b1.HasKey("ShadowverseCardEntryId");
|
||||
|
||||
b1.ToTable("CardEntry");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("ShadowverseCardEntryId");
|
||||
});
|
||||
|
||||
b.Navigation("Class");
|
||||
|
||||
b.Navigation("CollectionInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SVSim.Database.Models.ShadowverseDeckEntry", b =>
|
||||
{
|
||||
b.HasOne("SVSim.Database.Models.ClassEntry", "Class")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClassId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SVSim.Database.Models.LeaderSkinEntry", "LeaderSkin")
|
||||
.WithMany()
|
||||
.HasForeignKey("LeaderSkinId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SVSim.Database.Models.SleeveEntry", "Sleeve")
|
||||
.WithMany()
|
||||
.HasForeignKey("SleeveId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SVSim.Database.Models.Viewer", null)
|
||||
.WithMany("Decks")
|
||||
.HasForeignKey("ViewerId");
|
||||
|
||||
b.Navigation("Class");
|
||||
|
||||
b.Navigation("LeaderSkin");
|
||||
|
||||
b.Navigation("Sleeve");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SVSim.Database.Models.ClassEntry", b =>
|
||||
{
|
||||
b.Navigation("LeaderSkins");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using DCGEngine.Database.Models;
|
||||
using SVSim.Database.Common;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using DCGEngine.Database.Models;
|
||||
using SVSim.Database.Common;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using DCGEngine.Database.Models;
|
||||
using SVSim.Database.Common;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using DCGEngine.Database.Models;
|
||||
using SVSim.Database.Common;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using DCGEngine.Database.Models;
|
||||
using SVSim.Database.Common;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using DCGEngine.Database.Models;
|
||||
using SVSim.Database.Common;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using DCGEngine.Database.Models;
|
||||
using SVSim.Database.Common;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using DCGEngine.Database.Models;
|
||||
using SVSim.Database.Common;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using DCGEngine.Database.Models;
|
||||
using SVSim.Database.Common;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using DCGEngine.Database.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
@@ -9,7 +8,7 @@ namespace SVSim.Database.Models;
|
||||
[Owned]
|
||||
public class OwnedCardEntry
|
||||
{
|
||||
public CardEntry Card { get; set; } = new ShadowverseCardEntry();
|
||||
public ShadowverseCardEntry Card { get; set; } = new ShadowverseCardEntry();
|
||||
public int Count { get; set; }
|
||||
public bool IsProtected { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using DCGEngine.Database.Models;
|
||||
using SVSim.Database.Common;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
|
||||
|
||||
@@ -1,12 +1,32 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using DCGEngine.Database.Models;
|
||||
using SVSim.Database.Common;
|
||||
using SVSim.Database.Enums;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
|
||||
public class ShadowverseCardEntry : CardEntry
|
||||
public class ShadowverseCardEntry : BaseEntity<long>
|
||||
{
|
||||
/// <summary>
|
||||
/// The internal name of this card (not the localized display name).
|
||||
/// </summary>
|
||||
[Required(AllowEmptyStrings = false)]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Attack stat (atk on the wire).
|
||||
/// </summary>
|
||||
public int? Attack { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Life / defense stat (life on the wire).
|
||||
/// </summary>
|
||||
public int? Defense { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Play cost (cost on the wire).
|
||||
/// </summary>
|
||||
public int? PrimaryResourceCost { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The rarity of this card.
|
||||
/// </summary>
|
||||
@@ -24,9 +44,9 @@ public class ShadowverseCardEntry : CardEntry
|
||||
#region Navigation Properties
|
||||
|
||||
/// <summary>
|
||||
/// The class this card belongs to, or optionally none for neutral cards.
|
||||
/// The class this card belongs to, or null for neutral cards.
|
||||
/// </summary>
|
||||
public ClassEntry? Class { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using DCGEngine.Database.Models;
|
||||
using SVSim.Database.Common;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
|
||||
public class ShadowverseCardSetEntry : CardSetEntry
|
||||
public class ShadowverseCardSetEntry : BaseEntity<int>
|
||||
{
|
||||
/// <summary>
|
||||
/// The internal name of the set.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The cards in the set.
|
||||
/// </summary>
|
||||
public List<ShadowverseCardEntry> Cards { get; set; } = new List<ShadowverseCardEntry>();
|
||||
|
||||
public bool IsInRotation { get; set; }
|
||||
public bool IsBasic { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
using DCGEngine.Database.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using SVSim.Database.Common;
|
||||
using SVSim.Database.Enums;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
|
||||
public class ShadowverseDeckEntry : DeckEntry
|
||||
public class ShadowverseDeckEntry : BaseEntity<Guid>
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal deck name.
|
||||
/// </summary>
|
||||
[Required(AllowEmptyStrings = false)]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Cards in this deck.
|
||||
/// </summary>
|
||||
public List<DeckCard> Cards { get; set; } = new List<DeckCard>();
|
||||
|
||||
public int Number { get; set; }
|
||||
public Format Format { get; set; }
|
||||
public bool RandomLeaderSkin { get; set; }
|
||||
@@ -19,4 +30,4 @@ public class ShadowverseDeckEntry : DeckEntry
|
||||
public LeaderSkinEntry LeaderSkin { get; set; } = new LeaderSkinEntry();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using DCGEngine.Database.Models;
|
||||
using SVSim.Database.Common;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using DCGEngine.Database.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SVSim.Database.Enums;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using DCGEngine.Database.Models;
|
||||
using SVSim.Database.Common;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using DCGEngine.Database.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SVSim.Database.Models;
|
||||
|
||||
@@ -19,7 +18,7 @@ public class CardRepository : BaseRepository<ShadowverseCardEntry>, ICardReposit
|
||||
public async Task<List<ShadowverseCardEntry>> GetAllBasic()
|
||||
{
|
||||
return await DbContext.Set<ShadowverseCardSetEntry>().Where(set => set.IsBasic).SelectMany(set => set.Cards)
|
||||
.Cast<ShadowverseCardEntry>().ToListAsync();
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<List<ShadowverseCardSetEntry>> GetCardSets(bool onlyInRotation)
|
||||
|
||||
@@ -19,11 +19,12 @@ public class ViewerRepository : IViewerRepository
|
||||
|
||||
public async Task<Models.Viewer?> GetViewerBySocialConnection(SocialAccountType accountType, ulong socialId)
|
||||
{
|
||||
return (await _dbContext.Set<SocialAccountConnection>()
|
||||
.AsNoTracking()
|
||||
.Include(sac => sac.Viewer)
|
||||
.FirstOrDefaultAsync(sac => sac.AccountType == accountType && sac.AccountId == socialId))
|
||||
?.Viewer;
|
||||
// SocialAccountConnection is [Owned]-by-Viewer — can't be queried as a top-level Set<T>.
|
||||
// Look up the Viewer that has a matching owned connection instead.
|
||||
return await _dbContext.Set<Models.Viewer>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(v => v.SocialAccountConnections.Any(sac =>
|
||||
sac.AccountType == accountType && sac.AccountId == socialId));
|
||||
}
|
||||
|
||||
public async Task<Models.Viewer?> GetViewerWithSocials(long id)
|
||||
@@ -32,10 +33,31 @@ public class ViewerRepository : IViewerRepository
|
||||
.FirstOrDefaultAsync(viewer => viewer.Id == id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads a viewer with every navigation property needed to render the home-screen
|
||||
/// (/load/index). Heavy query — only call from LoadController.Index.
|
||||
/// </summary>
|
||||
public async Task<Models.Viewer?> GetViewerByShortUdid(long shortUdid)
|
||||
{
|
||||
return await _dbContext.Set<Models.Viewer>().AsNoTracking().Include(viewer => viewer.MissionData)
|
||||
.Include(viewer => viewer.Info).FirstOrDefaultAsync(viewer => viewer.ShortUdid == shortUdid);
|
||||
return await _dbContext.Set<Models.Viewer>()
|
||||
.AsNoTracking()
|
||||
.Include(v => v.MissionData)
|
||||
.Include(v => v.Info).ThenInclude(i => i.SelectedEmblem)
|
||||
.Include(v => v.Info).ThenInclude(i => i.SelectedDegree)
|
||||
.Include(v => v.Currency)
|
||||
.Include(v => v.Classes).ThenInclude(c => c.Class).ThenInclude(c => c.LeaderSkins)
|
||||
.Include(v => v.Classes).ThenInclude(c => c.LeaderSkin)
|
||||
.Include(v => v.Decks).ThenInclude(d => d.Class)
|
||||
.Include(v => v.Decks).ThenInclude(d => d.Sleeve)
|
||||
.Include(v => v.Decks).ThenInclude(d => d.LeaderSkin)
|
||||
.Include(v => v.Cards).ThenInclude(c => c.Card)
|
||||
.Include(v => v.Items).ThenInclude(i => i.Item)
|
||||
.Include(v => v.Sleeves)
|
||||
.Include(v => v.Emblems)
|
||||
.Include(v => v.Degrees)
|
||||
.Include(v => v.LeaderSkins).ThenInclude(ls => ls.Class)
|
||||
.Include(v => v.MyPageBackgrounds)
|
||||
.FirstOrDefaultAsync(viewer => viewer.ShortUdid == shortUdid);
|
||||
}
|
||||
|
||||
public async Task<Models.Viewer> RegisterViewer(string displayName, SocialAccountType socialType,
|
||||
@@ -46,7 +68,7 @@ public class ViewerRepository : IViewerRepository
|
||||
DisplayName = displayName
|
||||
};
|
||||
GameConfiguration gameConfig = await new GlobalsRepository(_dbContext).GetGameConfiguration("default");
|
||||
|
||||
|
||||
viewer.SocialAccountConnections.Add(new SocialAccountConnection
|
||||
{
|
||||
AccountId = socialAccountIdentifier,
|
||||
@@ -61,23 +83,40 @@ public class ViewerRepository : IViewerRepository
|
||||
viewer.Currency.RedEther = gameConfig.DefaultEther;
|
||||
viewer.MissionData.TutorialState = 100; // finishes tutorial for now
|
||||
|
||||
List<ClassEntry> classes = await _dbContext.Set<ClassEntry>().ToListAsync();
|
||||
viewer.Classes = classes.Select(ce => new ViewerClassData
|
||||
// Load classes WITH their LeaderSkins — DefaultLeaderSkin iterates the nav collection
|
||||
// and would otherwise be null (audit §6 #3 latent NRE — this is the one).
|
||||
List<ClassEntry> classes = await _dbContext.Set<ClassEntry>()
|
||||
.Include(c => c.LeaderSkins)
|
||||
.ToListAsync();
|
||||
|
||||
viewer.Classes = classes.Select(ce =>
|
||||
{
|
||||
Class = ce,
|
||||
Exp = 0,
|
||||
Level = 0,
|
||||
LeaderSkin = ce.DefaultLeaderSkin
|
||||
var skin = ce.DefaultLeaderSkin ?? ce.LeaderSkins.FirstOrDefault();
|
||||
return new ViewerClassData
|
||||
{
|
||||
Class = ce,
|
||||
Exp = 0,
|
||||
Level = 0,
|
||||
LeaderSkin = skin ?? new LeaderSkinEntry { Id = 0, Name = "<missing>", ClassId = ce.Id }
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
viewer.Sleeves.Add(gameConfig.DefaultSleeve);
|
||||
viewer.Degrees.Add(gameConfig.DefaultDegree);
|
||||
viewer.Emblems.Add(gameConfig.DefaultEmblem);
|
||||
viewer.MyPageBackgrounds.Add(gameConfig.DefaultMyPageBackground);
|
||||
viewer.LeaderSkins.AddRange(viewer.Classes.Select(vcd => vcd.LeaderSkin));
|
||||
if (gameConfig.DefaultSleeve is not null) viewer.Sleeves.Add(gameConfig.DefaultSleeve);
|
||||
if (gameConfig.DefaultDegree is not null) viewer.Degrees.Add(gameConfig.DefaultDegree);
|
||||
if (gameConfig.DefaultEmblem is not null) viewer.Emblems.Add(gameConfig.DefaultEmblem);
|
||||
if (gameConfig.DefaultMyPageBackground is not null) viewer.MyPageBackgrounds.Add(gameConfig.DefaultMyPageBackground);
|
||||
|
||||
// Grant one of each class's default leader skin. Filter out the synthetic placeholders
|
||||
// (Id=0) and dedupe — skins are many-to-many via SleeveEntryViewer-style join.
|
||||
var grantedSkins = viewer.Classes
|
||||
.Select(vcd => vcd.LeaderSkin)
|
||||
.Where(s => s.Id != 0)
|
||||
.DistinctBy(s => s.Id)
|
||||
.ToList();
|
||||
viewer.LeaderSkins.AddRange(grantedSkins);
|
||||
|
||||
_dbContext.Set<Models.Viewer>().Add(viewer);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
return viewer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,16 +6,11 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DCGEngine.Database\DCGEngine.Database.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Data\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CsvHelper" Version="33.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.8" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,30 +1,108 @@
|
||||
using DCGEngine.Database;
|
||||
using DCGEngine.Database.Configuration;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using SVSim.Database.Common;
|
||||
using SVSim.Database.DataSeeders;
|
||||
using SVSim.Database.Models;
|
||||
|
||||
namespace SVSim.Database;
|
||||
|
||||
public class SVSimDbContext : DCGEDbContext
|
||||
public class SVSimDbContext : DbContext
|
||||
{
|
||||
public SVSimDbContext(IOptions<DCGEDatabaseConfiguration> configuration, ILogger<DCGEDbContext> logger, DbContextOptions options) : base(configuration, logger, options)
|
||||
private readonly ILogger<SVSimDbContext> _logger;
|
||||
|
||||
public SVSimDbContext(ILogger<SVSimDbContext> logger, DbContextOptions<SVSimDbContext> options) : base(options)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
#region DbSets
|
||||
|
||||
public DbSet<Viewer> Viewers => Set<Viewer>();
|
||||
|
||||
public DbSet<ShadowverseCardEntry> Cards => Set<ShadowverseCardEntry>();
|
||||
public DbSet<ShadowverseCardSetEntry> CardSets => Set<ShadowverseCardSetEntry>();
|
||||
public DbSet<ShadowverseDeckEntry> Decks => Set<ShadowverseDeckEntry>();
|
||||
|
||||
public DbSet<ClassEntry> Classes => Set<ClassEntry>();
|
||||
public DbSet<ClassExpEntry> ClassExpCurve => Set<ClassExpEntry>();
|
||||
public DbSet<LeaderSkinEntry> LeaderSkins => Set<LeaderSkinEntry>();
|
||||
public DbSet<SleeveEntry> Sleeves => Set<SleeveEntry>();
|
||||
public DbSet<EmblemEntry> Emblems => Set<EmblemEntry>();
|
||||
public DbSet<DegreeEntry> Degrees => Set<DegreeEntry>();
|
||||
public DbSet<MyPageBackgroundEntry> MyPageBackgrounds => Set<MyPageBackgroundEntry>();
|
||||
public DbSet<BattlefieldEntry> Battlefields => Set<BattlefieldEntry>();
|
||||
public DbSet<RankInfoEntry> RankInfo => Set<RankInfoEntry>();
|
||||
public DbSet<ItemEntry> Items => Set<ItemEntry>();
|
||||
|
||||
public DbSet<GameConfiguration> GameConfigurations => Set<GameConfiguration>();
|
||||
|
||||
#endregion
|
||||
|
||||
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
foreach (var entityEntry in ChangeTracker.Entries())
|
||||
{
|
||||
if (entityEntry.Entity is ITimeTrackedEntity timeTrackedEntity)
|
||||
{
|
||||
if (entityEntry.State is EntityState.Added && timeTrackedEntity.DateCreated == DateTime.MinValue)
|
||||
{
|
||||
timeTrackedEntity.DateCreated = DateTime.UtcNow;
|
||||
}
|
||||
if (entityEntry.State is EntityState.Modified or EntityState.Added)
|
||||
{
|
||||
timeTrackedEntity.DateUpdated = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return await base.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
modelBuilder.Entity<ShadowverseDeckEntry>()
|
||||
.OwnsMany(de => de.Cards);
|
||||
|
||||
// For whatever reason it cannot figure out this relationship on it's own
|
||||
// BaseEntity<TKey> annotates Id with [DatabaseGenerated(None)] for the integer-PK
|
||||
// entities seeded via HasData. ShadowverseDeckEntry uses Guid and is created at
|
||||
// runtime — without client-side generation every new deck gets Guid.Empty and the
|
||||
// second deck insert collides on PK. (DDL has no column default; this only works
|
||||
// because EF generates a sequential Guid before INSERT.)
|
||||
modelBuilder.Entity<ShadowverseDeckEntry>()
|
||||
.Property(d => d.Id)
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
// EF can't figure this many-to-many out on its own
|
||||
modelBuilder.Entity<SleeveEntry>()
|
||||
.HasMany<Viewer>(se => se.Viewers)
|
||||
.HasMany(se => se.Viewers)
|
||||
.WithMany(v => v.Sleeves);
|
||||
|
||||
modelBuilder.HasSequence<long>("ShortUdidSequence").StartsAt(400000000);
|
||||
modelBuilder.Entity<Viewer>()
|
||||
.Property(v => v.ShortUdid)
|
||||
.UseSequence("ShortUdidSequence");
|
||||
|
||||
new BaseDataSeeder().Seed(modelBuilder);
|
||||
new DefaultSettingsSeeder().Seed(modelBuilder);
|
||||
|
||||
base.OnModelCreating(modelBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateDatabase()
|
||||
{
|
||||
IEnumerable<string> pendingMigrations = Database.GetPendingMigrations();
|
||||
if (!pendingMigrations.Any())
|
||||
{
|
||||
_logger.LogDebug("No pending migrations found, continuing.");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (string migration in pendingMigrations)
|
||||
{
|
||||
_logger.LogInformation("Found pending migration with name {migrationName}.", migration);
|
||||
}
|
||||
_logger.LogInformation("Attempting to apply pending migrations...");
|
||||
Database.Migrate();
|
||||
_logger.LogInformation("Migrations applied.");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user