using System.Linq.Expressions; using System.Reflection; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Pf2eModel.Model; using Pf2eModel.Model.Base; using Pf2eModel.Model.Base.Keywords; using Pf2eModel.Model.Base.Source; using Pf2eModel.Model.Base.Targets; using Pf2eModel.Model.Common; using Pf2eModel.Model.Common.I18n; using Pf2eModel.Model.Enum; using Pf2eModel.Model.Structure; using Pf2eModel.Model.Structure.AncestryRelation; using Pf2eModel.Model.Structure.ArchetypeRelation; using Pf2eModel.Model.Structure.BackgroundRelation; using Pf2eModel.Model.Structure.ClassRelation; using Pf2eModel.Model.Structure.DeityRelation; using Pf2eModel.Model.Structure.FeatRelation; using Pf2eModel.Model.Structure.SpellRelation; namespace Pf2eModel; public partial class Pf2eDbContext { public const int NameLength = 255; protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.UseIdentityByDefaultColumns(); DefineEnums(modelBuilder); DefineBase(modelBuilder); DefineCommon(modelBuilder); DefineStructure(modelBuilder); DefineMethods(modelBuilder); } private void BindEntity(EntityTypeBuilder entity) where T : class, IEntity { entity .HasOne(entity => entity.Entity) .WithOne() .HasForeignKey(entity => entity.Id) .IsRequired(); } private void BindLocaleString( EntityTypeBuilder entity, Expression> func ) where T : class { /* entity.OwnsMany(func, d => { d.ToJson(); }); */ entity .Property(func) .HasConversion(v => v.Serialize(), v => LocaleString.Deserialize(v)) .HasColumnType("jsonb"); } private void DefineEnums(ModelBuilder modelBuilder) { modelBuilder .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum(); } private void DefineBase(ModelBuilder modelBuilder) { modelBuilder.Entity(entity => { BindLocaleString(entity, e => e.Name); entity.HasMany(e => e.Traits).WithMany(t => t.Entities); entity.HasMany(e => e.Keywords).WithMany(t => t.Entities); // EntitySource entity .HasMany(e => e.Sources) .WithOne(e => e.Entity) .HasForeignKey(e => e.EntityId) .OnDelete(DeleteBehavior.Cascade); // EntityExternal entity .HasMany(e => e.Externals) .WithOne(e => e.Entity) .HasForeignKey(e => e.EntityId) .OnDelete(DeleteBehavior.Cascade); }); // Source modelBuilder.Entity(entity => { entity.Property(e => e.Id).HasColumnName("id"); entity .HasMany(s => s.EntitySources) .WithOne(e => e.Source) .HasForeignKey(e => e.SourceId) .OnDelete(DeleteBehavior.Cascade); // + EntitySource }); modelBuilder.Entity(entity => { }); // External modelBuilder.Entity(entity => { entity .HasMany(t => t.EntityExternals) .WithOne(e => e.Target) .HasForeignKey(e => e.TargetId) .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity(entity => { }); // Keywords modelBuilder.Entity(entity => { entity .HasMany(e => e.Aliases) .WithOne(e => e.Keyword) .HasForeignKey(e => e.KeywordId) .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity(entity => { }); } private void DefineCommon(ModelBuilder modelBuilder) { modelBuilder.Entity(entity => { }); modelBuilder.Entity(entity => { }); modelBuilder.Entity(entity => { }); } private void DefineStructure(ModelBuilder modelBuilder) { // Action modelBuilder.Entity(entity => { BindEntity(entity); }); // Ancestry modelBuilder.Entity(entity => { BindEntity(entity); entity .HasMany(e => e.AncestryBoosts) .WithOne(e => e.Ancestry) .HasForeignKey(e => e.AncestryId) .OnDelete(DeleteBehavior.Cascade); entity .HasMany(e => e.AncestryItems) .WithOne(e => e.Ancestry) .HasForeignKey(e => e.AncestryId) .OnDelete(DeleteBehavior.Cascade); entity .HasMany(e => e.AncestryLanguages) .WithOne(e => e.Ancestry) .HasForeignKey(e => e.AncestryId) .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity(entity => { }); modelBuilder.Entity(entity => { entity .HasOne(d => d.Item) .WithMany() .HasForeignKey(d => d.ItemId) .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity(entity => { entity .HasOne(d => d.Language) .WithMany() .HasForeignKey(d => d.LanguageId) .OnDelete(DeleteBehavior.Cascade); }); // Archetype modelBuilder.Entity(entity => { BindEntity(entity); entity .HasOne(a => a.DedicationFeat) .WithOne() .HasForeignKey(a => a.DedicationFeatId) .OnDelete(DeleteBehavior.Cascade); entity .HasMany(a => a.Feats) .WithMany(f => f.Archetypes) .UsingEntity( l => l.HasOne().WithMany().HasForeignKey(af => af.FeatId), r => r.HasOne().WithMany().HasForeignKey(af => af.ArchetypeId) ); }); // Background modelBuilder.Entity(entity => { BindEntity(entity); }); modelBuilder.Entity(entity => { entity .HasOne(d => d.Background) .WithMany(p => p.BackgroundBoosts) .HasForeignKey(d => d.BackgroundId) .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity(entity => { entity .HasOne(d => d.Background) .WithMany(p => p.BackgroundItems) .HasForeignKey(d => d.BackgroundId) .OnDelete(DeleteBehavior.Cascade); entity .HasOne(d => d.Entity) .WithMany() .HasForeignKey(d => d.EntityId) .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity(entity => { entity .HasOne(d => d.Background) .WithMany(p => p.BackgroundSkills) .HasForeignKey(d => d.BackgroundId) .OnDelete(DeleteBehavior.Cascade); }); // Class modelBuilder.Entity(entity => { BindEntity(entity); }); modelBuilder.Entity(entity => { entity .HasOne(d => d.Class) .WithMany(p => p.ClassKeyAbilities) .HasForeignKey(d => d.ClassId) .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity(entity => { entity .HasOne(d => d.Class) .WithMany(p => p.ClassProgressions) .HasForeignKey(d => d.ClassId) .OnDelete(DeleteBehavior.Cascade); entity .HasOne(d => d.Entity) .WithMany() .HasForeignKey(d => d.EntityId) .OnDelete(DeleteBehavior.SetNull); }); modelBuilder.Entity(entity => { entity .HasOne(d => d.Class) .WithMany(p => p.ClassTrainings) .HasForeignKey(d => d.ClassId) .OnDelete(DeleteBehavior.Cascade); entity .HasOne(d => d.Training) .WithMany() .HasForeignKey(d => d.TrainingId) .OnDelete(DeleteBehavior.SetNull); }); // Subclass modelBuilder.Entity(entity => { BindEntity(entity); entity .HasOne(g => g.Class) .WithMany(c => c.SubclassGroups) .HasForeignKey(g => g.ClassId) .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity(entity => { BindEntity(entity); BindLocaleString(entity, s => s.Additional); entity .HasOne(s => s.Group) .WithMany(g => g.Subclasses) .HasForeignKey(s => s.GroupId) .OnDelete(DeleteBehavior.Cascade); }); // Condition modelBuilder.Entity(entity => { BindEntity(entity); }); // Deity modelBuilder.Entity(entity => { BindEntity(entity); entity .HasMany(e => e.DeityAttributes) .WithOne(e => e.Deity) .HasForeignKey(e => e.DeityId) .OnDelete(DeleteBehavior.Cascade); entity .HasMany(e => e.DeityDomains) .WithOne(e => e.Deity) .HasForeignKey(e => e.DeityId) .OnDelete(DeleteBehavior.Cascade); entity .HasMany(e => e.DeitySpells) .WithOne(e => e.Deity) .HasForeignKey(e => e.DeityId) .OnDelete(DeleteBehavior.Cascade); entity .HasMany(e => e.DeityWeapons) .WithOne(e => e.Deity) .HasForeignKey(e => e.DeityId) .OnDelete(DeleteBehavior.Cascade); entity.HasMany(e => e.Skills).WithMany(); }); modelBuilder.Entity(entity => { }); modelBuilder.Entity(entity => { }); modelBuilder.Entity(entity => { entity .HasOne(d => d.Spell) .WithMany() .HasForeignKey(d => d.SpellId) .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity(entity => { }); // Feat modelBuilder.Entity(entity => { BindEntity(entity); entity .HasMany(e => e.FeatPrerequisites) .WithOne(e => e.Feat) .HasForeignKey(e => e.FeatId) .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity(entity => { }); // Heritage modelBuilder.Entity(entity => { BindEntity(entity); entity .HasOne(d => d.Ancestry) .WithMany(p => p.Heritages) .HasForeignKey(d => d.AncestryId) .OnDelete(DeleteBehavior.Cascade); }); // Spell modelBuilder.Entity(entity => { BindEntity(entity); entity .HasOne(e => e.SpellArea) .WithOne(e => e.Spell) .HasForeignKey(e => e.SpellId) .OnDelete(DeleteBehavior.Cascade) .IsRequired(false); entity .HasOne(e => e.SpellRitual) .WithOne(e => e.Spell) .HasForeignKey(e => e.SpellId) .OnDelete(DeleteBehavior.Cascade) .IsRequired(false); entity .HasMany(e => e.SpellDamages) .WithOne(e => e.Spell) .HasForeignKey(e => e.SpellId) .OnDelete(DeleteBehavior.Cascade); entity .HasMany(e => e.SpellDefenses) .WithOne(e => e.Spell) .HasForeignKey(e => e.SpellId) .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity(entity => { }); modelBuilder.Entity(entity => { }); modelBuilder.Entity(entity => { }); modelBuilder.Entity(entity => { }); // Trait modelBuilder.Entity(entity => { BindEntity(entity); }); } private void DefineMethods(ModelBuilder modelBuilder) { MethodInfo localeSearch = typeof(Pf2eDbContext).GetMethod( nameof(LocaleSearch), BindingFlags.Static | BindingFlags.Public, [typeof(LocaleString), typeof(string)] )!; modelBuilder.HasDbFunction( localeSearch, builder => { builder.HasName("locale_search"); builder.HasParameter("data").HasStoreType("jsonb"); builder.HasParameter("search_value"); } ); MethodInfo localeSearchArray = typeof(Pf2eDbContext).GetMethod( nameof(LocaleSearchArray), BindingFlags.Static | BindingFlags.Public, [typeof(LocaleString), typeof(IEnumerable)] )!; modelBuilder.HasDbFunction( localeSearchArray, builder => { builder.HasName("locale_search_array"); builder.HasParameter("data").HasStoreType("jsonb"); builder.HasParameter("search_values"); } ); } public static bool LocaleSearch(LocaleString data, string search_value) => throw new InvalidOperationException("Only for LINQ translation"); public static bool LocaleSearchArray(LocaleString data, IEnumerable search_values) => throw new InvalidOperationException("Only for LINQ translation"); }