561 lines
18 KiB
C#
561 lines
18 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
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.RitualRelation;
|
|
using Pf2eModel.Model.Structure.SpellRelation;
|
|
|
|
namespace Pf2eModel;
|
|
|
|
[SuppressMessage("ReSharper", "UnusedParameter.Local")]
|
|
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<T>(EntityTypeBuilder<T> entity)
|
|
where T : class, IEntity
|
|
{
|
|
entity.HasOne(e => e.Entity).WithOne().HasForeignKey<T>(e => e.Id).IsRequired();
|
|
}
|
|
|
|
private void BindLocaleString<T>(
|
|
EntityTypeBuilder<T> entity,
|
|
Expression<Func<T, LocaleString?>> func,
|
|
bool required = true
|
|
)
|
|
where T : class
|
|
{
|
|
entity
|
|
.Property(func)
|
|
.HasConversion(v => v.Serialize(), v => LocaleString.Deserialize(v))
|
|
.HasColumnType("jsonb")
|
|
.IsRequired(required);
|
|
}
|
|
|
|
private void DefineEnums(ModelBuilder modelBuilder)
|
|
{
|
|
modelBuilder
|
|
.HasPostgresEnum<ActionEnum>()
|
|
.HasPostgresEnum<EntityType>()
|
|
.HasPostgresEnum<EntityRarity>()
|
|
.HasPostgresEnum<LocaleType>()
|
|
.HasPostgresEnum<TraitType>()
|
|
.HasPostgresEnum<ActionCategory>()
|
|
.HasPostgresEnum<Size>()
|
|
.HasPostgresEnum<AncestryVision>()
|
|
.HasPostgresEnum<ClassProgressionType>()
|
|
.HasPostgresEnum<ConditionGroup>()
|
|
.HasPostgresEnum<DeityCategory>()
|
|
.HasPostgresEnum<DeityFonts>()
|
|
.HasPostgresEnum<DeitySanctification>()
|
|
.HasPostgresEnum<FeatActionType>()
|
|
.HasPostgresEnum<FeatCategory>()
|
|
.HasPostgresEnum<SpellAreaType>()
|
|
.HasPostgresEnum<SpellDamageCategory>()
|
|
.HasPostgresEnum<SpellDamageKinds>()
|
|
.HasPostgresEnum<SpellTradition>()
|
|
.HasPostgresEnum<AttributeValues>()
|
|
.HasPostgresEnum<TrainingRank>()
|
|
.HasPostgresEnum<SourceCategory>();
|
|
}
|
|
|
|
private void DefineBase(ModelBuilder modelBuilder)
|
|
{
|
|
modelBuilder.Entity<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<Source>(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<EntitySource>(entity => { });
|
|
|
|
// External
|
|
modelBuilder.Entity<ExternalTarget>(entity =>
|
|
{
|
|
entity
|
|
.HasMany(t => t.EntityExternals)
|
|
.WithOne(e => e.Target)
|
|
.HasForeignKey(e => e.TargetId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
});
|
|
modelBuilder.Entity<EntityExternal>(entity => { });
|
|
|
|
// Keywords
|
|
modelBuilder.Entity<Keyword>(entity =>
|
|
{
|
|
BindLocaleString(entity, k => k.Name);
|
|
|
|
entity
|
|
.HasMany(e => e.Aliases)
|
|
.WithOne(e => e.Keyword)
|
|
.HasForeignKey(e => e.KeywordId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
});
|
|
modelBuilder.Entity<KeywordAlias>(entity => { });
|
|
}
|
|
|
|
private void DefineCommon(ModelBuilder modelBuilder)
|
|
{
|
|
modelBuilder.Entity<Frequency>(entity => { });
|
|
|
|
modelBuilder.Entity<Language>(entity => { });
|
|
|
|
modelBuilder.Entity<Training>(entity =>
|
|
{
|
|
BindLocaleString(entity, t => t.Name);
|
|
});
|
|
}
|
|
|
|
private void DefineStructure(ModelBuilder modelBuilder)
|
|
{
|
|
// Action
|
|
modelBuilder.Entity<Model.Structure.Action>(entity =>
|
|
{
|
|
BindEntity(entity);
|
|
});
|
|
|
|
// Ancestry
|
|
modelBuilder.Entity<Ancestry>(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<AncestryBoost>(entity => { });
|
|
modelBuilder.Entity<AncestryItem>(entity =>
|
|
{
|
|
entity
|
|
.HasOne(d => d.Item)
|
|
.WithMany()
|
|
.HasForeignKey(d => d.ItemId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
});
|
|
modelBuilder.Entity<AncestryLanguage>(entity =>
|
|
{
|
|
entity
|
|
.HasOne(d => d.Language)
|
|
.WithMany()
|
|
.HasForeignKey(d => d.LanguageId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
});
|
|
|
|
// Archetype
|
|
modelBuilder.Entity<Archetype>(entity =>
|
|
{
|
|
BindEntity(entity);
|
|
|
|
entity
|
|
.HasOne(a => a.DedicationFeat)
|
|
.WithOne()
|
|
.HasForeignKey<Archetype>(a => a.DedicationFeatId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
entity
|
|
.HasMany(a => a.Feats)
|
|
.WithMany(f => f.Archetypes)
|
|
.UsingEntity<ArchetypeFeats>(
|
|
l => l.HasOne<Feat>().WithMany().HasForeignKey(af => af.FeatId),
|
|
r => r.HasOne<Archetype>().WithMany().HasForeignKey(af => af.ArchetypeId)
|
|
);
|
|
});
|
|
|
|
// Background
|
|
modelBuilder.Entity<Background>(entity =>
|
|
{
|
|
BindEntity(entity);
|
|
});
|
|
modelBuilder.Entity<BackgroundBoost>(entity =>
|
|
{
|
|
entity
|
|
.HasOne(d => d.Background)
|
|
.WithMany(p => p.BackgroundBoosts)
|
|
.HasForeignKey(d => d.BackgroundId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
});
|
|
modelBuilder.Entity<BackgroundItem>(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<BackgroundTraining>(entity =>
|
|
{
|
|
entity
|
|
.HasOne(d => d.Background)
|
|
.WithMany(p => p.BackgroundSkills)
|
|
.HasForeignKey(d => d.BackgroundId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
});
|
|
|
|
// Class
|
|
modelBuilder.Entity<Class>(entity =>
|
|
{
|
|
BindEntity(entity);
|
|
});
|
|
modelBuilder.Entity<ClassKeyAbility>(entity =>
|
|
{
|
|
entity
|
|
.HasOne(d => d.Class)
|
|
.WithMany(p => p.ClassKeyAbilities)
|
|
.HasForeignKey(d => d.ClassId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
});
|
|
modelBuilder.Entity<ClassProgression>(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<ClassTraining>(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<SubclassGroup>(entity =>
|
|
{
|
|
BindEntity(entity);
|
|
|
|
entity
|
|
.HasOne(g => g.Class)
|
|
.WithMany(c => c.SubclassGroups)
|
|
.HasForeignKey(g => g.ClassId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
});
|
|
modelBuilder.Entity<Subclass>(entity =>
|
|
{
|
|
BindEntity(entity);
|
|
|
|
BindLocaleString(entity, s => s.Additional);
|
|
|
|
entity
|
|
.HasOne(s => s.Group)
|
|
.WithMany(g => g.Subclasses)
|
|
.HasForeignKey(s => s.GroupId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
entity
|
|
.HasMany(e => e.Entities)
|
|
.WithMany()
|
|
.UsingEntity<SubclassProvidedEntities>(
|
|
l => l.HasOne<Entity>().WithMany().HasForeignKey(se => se.EntityId),
|
|
r => r.HasOne<Subclass>().WithMany().HasForeignKey(se => se.SubclassId)
|
|
);
|
|
|
|
entity
|
|
.HasMany(e => e.Trainings)
|
|
.WithOne(t => t.Subclass)
|
|
.HasForeignKey(t => t.SubclassId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
});
|
|
modelBuilder.Entity<SubclassTrainings>(entity =>
|
|
{
|
|
entity
|
|
.HasOne(d => d.Subclass)
|
|
.WithMany(p => p.Trainings)
|
|
.HasForeignKey(d => d.SubclassId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
});
|
|
|
|
// Condition
|
|
modelBuilder.Entity<Condition>(entity =>
|
|
{
|
|
BindEntity(entity);
|
|
});
|
|
|
|
// Deity
|
|
modelBuilder.Entity<Deity>(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<DeityAttribute>(entity => { });
|
|
modelBuilder.Entity<DeityDomain>(entity => { });
|
|
modelBuilder.Entity<DeitySpell>(entity =>
|
|
{
|
|
entity
|
|
.HasOne(d => d.Spell)
|
|
.WithMany()
|
|
.HasForeignKey(d => d.SpellId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
});
|
|
modelBuilder.Entity<DeityWeapon>(entity => { });
|
|
|
|
// Feat
|
|
modelBuilder.Entity<Feat>(entity =>
|
|
{
|
|
BindEntity(entity);
|
|
|
|
entity
|
|
.HasMany(e => e.FeatPrerequisites)
|
|
.WithOne(e => e.Feat)
|
|
.HasForeignKey(e => e.FeatId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
});
|
|
modelBuilder.Entity<FeatPrerequisite>(entity => { });
|
|
|
|
// Heritage
|
|
modelBuilder.Entity<Heritage>(entity =>
|
|
{
|
|
BindEntity(entity);
|
|
|
|
entity
|
|
.HasOne(d => d.Ancestry)
|
|
.WithMany(p => p.Heritages)
|
|
.HasForeignKey(d => d.AncestryId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
});
|
|
|
|
// Spell
|
|
modelBuilder.Entity<Spell>(entity =>
|
|
{
|
|
BindEntity(entity);
|
|
|
|
BindLocaleString(entity, s => s.Trigger, false);
|
|
BindLocaleString(entity, s => s.Target, false);
|
|
BindLocaleString(entity, s => s.Cost, false);
|
|
BindLocaleString(entity, s => s.Requirement, false);
|
|
|
|
entity
|
|
.HasMany(e => e.SpellAreas)
|
|
.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<SpellArea>(entity =>
|
|
{
|
|
BindLocaleString(entity, a => a.Details, false);
|
|
});
|
|
modelBuilder.Entity<SpellDefense>(entity => { });
|
|
|
|
// Ritual
|
|
modelBuilder.Entity<Ritual>(entity =>
|
|
{
|
|
BindEntity(entity);
|
|
|
|
BindLocaleString(entity, s => s.RangeString, false);
|
|
BindLocaleString(entity, s => s.Target, false);
|
|
BindLocaleString(entity, s => s.Cost, false);
|
|
BindLocaleString(entity, s => s.Requirement, false);
|
|
BindLocaleString(entity, s => s.PrimaryCheckSuffix, false);
|
|
BindLocaleString(entity, s => s.SecondaryCastersSuffix, false);
|
|
|
|
entity
|
|
.HasOne(r => r.Area)
|
|
.WithOne(a => a.Ritual)
|
|
.HasForeignKey<RitualArea>(a => a.RitualId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
entity
|
|
.HasMany(r => r.Checks)
|
|
.WithOne(c => c.Ritual)
|
|
.HasForeignKey(c => c.RitualId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
});
|
|
modelBuilder.Entity<RitualArea>(entity =>
|
|
{
|
|
BindLocaleString(entity, a => a.Details, false);
|
|
});
|
|
|
|
modelBuilder.Entity<RitualCheck>(entity =>
|
|
{
|
|
BindLocaleString(entity, a => a.TrainingString, false);
|
|
|
|
entity
|
|
.HasOne(c => c.Training)
|
|
.WithMany()
|
|
.HasForeignKey(c => c.TrainingId)
|
|
.OnDelete(DeleteBehavior.SetNull);
|
|
});
|
|
|
|
// Trait
|
|
modelBuilder.Entity<Trait>(entity =>
|
|
{
|
|
BindEntity(entity);
|
|
});
|
|
|
|
// Rule
|
|
modelBuilder.Entity<Rule>(entity =>
|
|
{
|
|
BindEntity(entity);
|
|
|
|
entity
|
|
.HasOne(r => r.Parent)
|
|
.WithMany(r => r.Children)
|
|
.HasForeignKey(r => r.ParentId)
|
|
.IsRequired(false);
|
|
});
|
|
}
|
|
|
|
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<string>)]
|
|
)!;
|
|
modelBuilder.HasDbFunction(
|
|
localeSearchArray,
|
|
builder =>
|
|
{
|
|
builder.HasName("locale_search_array");
|
|
builder.HasParameter("data").HasStoreType("jsonb");
|
|
builder.HasParameter("search_values");
|
|
}
|
|
);
|
|
}
|
|
|
|
// ReSharper disable once InconsistentNaming
|
|
public static bool LocaleSearch(LocaleString data, string search_value) =>
|
|
throw new InvalidOperationException("Only for LINQ translation");
|
|
|
|
// ReSharper disable once InconsistentNaming
|
|
public static bool LocaleSearchArray(LocaleString data, IEnumerable<string> search_values) =>
|
|
throw new InvalidOperationException("Only for LINQ translation");
|
|
}
|