diff --git a/Pf2eDataScrapper/App/ProcessorAttribute.cs b/Pf2eDataScrapper/App/ProcessorAttribute.cs
index 0f3a04f..aa02c8d 100644
--- a/Pf2eDataScrapper/App/ProcessorAttribute.cs
+++ b/Pf2eDataScrapper/App/ProcessorAttribute.cs
@@ -22,15 +22,44 @@ public class ProcessorAttribute(string name) : Attribute
///
public ProcessorGroupEnum Group { get; init; } = ProcessorGroupEnum.General;
- ///
- /// Коллекция названий обработчиков, которые должны быть выполнены до этого оброботчика.
- ///
- public string? InnerType { get; init; } = null;
-
///
/// Процессор включён по умолчанию.
///
public bool Active { get; init; } = true;
+
+ ///
+ /// Разрешить выполнять процессов в параллельном режиме
+ ///
+ public bool Parallel { get; init; } = true;
+
+ ///
+ /// Обработчик набора элементов
+ ///
+ public virtual bool Collection { get; } = false;
+}
+
+///
+/// Аттрибут обработчика набор элементов.
+///
+/// Название обработчика.
+[AttributeUsage(AttributeTargets.Class)]
+public class ProcessorCollectionAttribute(string name) : ProcessorAttribute(name)
+{
+ ///
+ /// Название типов, который обрабатывает этот оброботчик.
+ /// null - обработать все типы.
+ ///
+ public required string? InnerType { get; init; }
+
+ ///
+ /// Тип используемого компендиума.
+ ///
+ public required ProcessorCompendiumEnum CompendiumType { get; init; }
+
+ ///
+ /// Обработчик набора элементов.
+ ///
+ public override bool Collection { get; } = true;
}
///
diff --git a/Pf2eDataScrapper/App/ProcessorCollection.cs b/Pf2eDataScrapper/App/ProcessorCollection.cs
index e2b7edf..bd7b61c 100644
--- a/Pf2eDataScrapper/App/ProcessorCollection.cs
+++ b/Pf2eDataScrapper/App/ProcessorCollection.cs
@@ -24,7 +24,7 @@ internal class ProcessorCollection
///
///
///
- public ProcessorCollectionItem this[string name] => Items.First(item => item.Name == name);
+ public ProcessorCollectionItem this[string name] => AllItems.First(item => item.Name == name);
///
/// Конструктор класса .
@@ -65,7 +65,19 @@ internal class ProcessorCollection
ProcessorDependenciesAttribute? dependenciesAttribute =
type.GetCustomAttribute();
- bool isCollection = processorAttribute.InnerType is not null;
+ bool isCollection = processorAttribute.Collection;
+
+ string? innerType = null;
+ ProcessorCompendiumEnum? compendium = null;
+
+ if (processorAttribute.GetType() == typeof(ProcessorCollectionAttribute))
+ {
+ ProcessorCollectionAttribute processorCollectionAttribute =
+ (ProcessorCollectionAttribute)processorAttribute;
+
+ innerType = processorCollectionAttribute.InnerType;
+ compendium = processorCollectionAttribute.CompendiumType;
+ }
return new()
{
@@ -74,8 +86,9 @@ internal class ProcessorCollection
Description = processorAttribute.Description,
Group = processorAttribute.Group,
Dependencies = dependenciesAttribute?.Dependencies ?? [],
- IsCollectionProcessor = isCollection,
- InnerType = processorAttribute.InnerType,
+ IsCollection = isCollection,
+ InnerType = innerType,
+ Compendium = compendium,
Active = processorAttribute.Active,
};
}
@@ -216,10 +229,16 @@ internal class ProcessorCollectionItem
/// Флаг, указывающий, обрабатывает ли обработчик коллекцию элементов.
/// Внутренний тип элементов указывается в .
///
- public required bool IsCollectionProcessor { get; init; }
+ public required bool IsCollection { get; init; }
///
/// Флаг, указывающий внутренний тип коллекции обрабатываемых элементов.
///
public string? InnerType { get; init; }
+
+ ///
+ /// Название типов, который обрабатывает этот оброботчик.
+ /// null - обработать все типы.
+ ///
+ public ProcessorCompendiumEnum? Compendium { get; init; }
}
diff --git a/Pf2eDataScrapper/App/ProcessorGroupEnum.cs b/Pf2eDataScrapper/App/ProcessorGroupEnum.cs
index a0c36e5..fcf7fc6 100644
--- a/Pf2eDataScrapper/App/ProcessorGroupEnum.cs
+++ b/Pf2eDataScrapper/App/ProcessorGroupEnum.cs
@@ -24,6 +24,12 @@ public enum ProcessorGroupEnum
FoundryEnEntities,
}
+public enum ProcessorCompendiumEnum
+{
+ Aonprd,
+ FoundryEn,
+}
+
public static class ProcessorGroupEnumExtension
{
public static string GetDescription(this ProcessorGroupEnum group)
diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/AbstractEntityProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/AbstractEntityProcessor.cs
index 5bfbe31..19f47c3 100644
--- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/AbstractEntityProcessor.cs
+++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/AbstractEntityProcessor.cs
@@ -13,6 +13,7 @@ using Pf2eDataScrapper.Processors;
using Pf2eModel;
using Pf2eModel.Model;
using Pf2eModel.Model.Base;
+using Pf2eModel.Model.Base.Legacy;
using Pf2eModel.Model.Base.Source;
using Pf2eModel.Model.Base.Targets;
using Pf2eModel.Model.Common.I18n;
@@ -160,8 +161,9 @@ internal abstract class AbstractEntityProcessor(
{
Entity = Entity,
Target = Globals.AonprdTarget,
+ ExternalId = Input.GetRequired("id"),
Suffix = Input.GetRequired("url"),
- Md5 = Md5(Input.Input.ToString()),
+ Md5 = Input.Md5(),
};
Context.EntityExternals.Add(external);
@@ -223,11 +225,6 @@ internal abstract class AbstractEntityProcessor(
return;
}
- if (legacyIdArray.Count > 1)
- {
- throw new("Несколько отметок ремастер-сущности на устаревшие!");
- }
-
string[] legacyIds = legacyIdArray.ToArray().Select(t => t.ToString()).ToArray();
if (legacyIds.Length == 0)
@@ -345,19 +342,5 @@ internal abstract class AbstractEntityProcessor(
return JsonConvert.SerializeObject(data, new JsonSerializerSettings());
}
- ///
- /// Создание контрольной суммы объекта.
- ///
- /// Строковое представление вычисляемого объекта.
- /// Использовать шестнадцатеричные цифры в нижнем регистре.
- /// Контрольная сумма объекта.
- public static string Md5(string input, bool isLowercase = false)
- {
- using MD5 md5 = MD5.Create();
- byte[] byteHash = md5.ComputeHash(Encoding.UTF8.GetBytes(input));
- string hash = BitConverter.ToString(byteHash).Replace("-", "");
- return isLowercase ? hash.ToLower() : hash;
- }
-
protected static readonly ConcurrentBag LegacyHandlers = [];
}
diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Common/LegacyProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Common/LegacyProcessor.cs
new file mode 100644
index 0000000..f0c311d
--- /dev/null
+++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Common/LegacyProcessor.cs
@@ -0,0 +1,125 @@
+using Newtonsoft.Json.Linq;
+using Pf2eDataScrapper.App;
+using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers;
+using Pf2eDataScrapper.Importers.Implementations.Aonprd.Helpers;
+using Pf2eDataScrapper.Processors;
+using Pf2eModel;
+using Pf2eModel.Model.Base.Legacy;
+using Pf2eModel.Model.Base.Source;
+using Pf2eModel.Model.Common.I18n;
+
+namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Common;
+
+[ProcessorCollection(
+ "aonprd:legacy",
+ InnerType = null,
+ CompendiumType = ProcessorCompendiumEnum.Aonprd,
+ Description = "Создание ссылок на устаревшие сущности",
+ Group = ProcessorGroupEnum.AonprdPreparation
+)]
+[ProcessorDependencies(["aonprd:source", "aonprd:external"])]
+internal class LegacyProcessor(
+ ProcessorInput input,
+ Pf2eDbContext context,
+ AonprdBuilderGlobals globals,
+ StringHelper stringHelper
+) : AbstractInputProcessor(input)
+{
+ public Pf2eDbContext Context { get; } = context;
+ public AonprdBuilderGlobals Globals { get; } = globals;
+ public StringHelper StringHelper { get; } = stringHelper;
+
+ private LegacyEntity LegacyEntity { get; set; } = null!;
+
+ public override void Process()
+ {
+ JArray? remasterIdArray = Input.GetOptional("remaster_id");
+
+ if (Input.GetOptional("remaster_id") is null)
+ {
+ throw new SilentSkipItemException();
+ }
+
+ LegacyEntity = new()
+ {
+ Name = { [LocaleType.English] = Input.GetOptional("name", "sidebar-29") },
+ };
+
+ AddSource();
+ AddExternal();
+ }
+
+ ///
+ /// Генерация источников сущности.
+ ///
+ protected virtual void AddSource()
+ {
+ foreach (LegacyEntitySource entitySource in GetSources())
+ {
+ LegacyEntity.Sources.Add(entitySource);
+ }
+ }
+
+ ///
+ /// Получение списка источников сущности.
+ ///
+ ///
+ public List GetSources()
+ {
+ string[] sourceNames = Input
+ .GetRequired("source")
+ .Select(token => token.ToString())
+ .ToArray();
+
+ if (sourceNames.Length == 0)
+ {
+ throw new(
+ $"Source for entity with id \"{LegacyEntity.Name[LocaleType.English]}\" "
+ + $"(Aonprd id \"{Input.GetRequired("id")}\") is not found"
+ );
+ }
+
+ List entitySources = [];
+
+ foreach (string sourceName in sourceNames)
+ {
+ Source source =
+ Context.Sources.FirstOrDefault(s => s.Name == sourceName)
+ ?? throw new($"Source \"{sourceName}\" did not found");
+
+ LegacyEntitySource entitySource = new()
+ {
+ LegacyEntity = LegacyEntity,
+ Source = source,
+ Suffix = StringHelper.ResolveSource(Input.GetRequired("source_markdown")),
+ IsRemaster = Input.GetOptional("legacy_id") is not null,
+ };
+
+ entitySources.Add(entitySource);
+ }
+
+ return entitySources;
+ }
+
+ ///
+ /// Генерация внешних ссылок сущности.
+ ///
+ protected virtual void AddExternal()
+ {
+ LegacyEntityExternal external = new()
+ {
+ LegacyEntity = LegacyEntity,
+ Target = Globals.AonprdTarget,
+ ExternalId = Input.GetRequired("id"),
+ Suffix = Input.GetRequired("url"),
+ Md5 = Input.Md5(),
+ };
+
+ Context.LegacyEntityExternals.Add(external);
+ }
+
+ public override void Save()
+ {
+ Context.LegacyEntities.Add(LegacyEntity);
+ }
+}
diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Common/SkillProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Common/SkillProcessor.cs
index 9c84231..03c9666 100644
--- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Common/SkillProcessor.cs
+++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Common/SkillProcessor.cs
@@ -7,8 +7,9 @@ using Pf2eModel.Model.Enum;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Common;
-[Processor(
+[ProcessorCollection(
"aonprd:skill",
+ CompendiumType = ProcessorCompendiumEnum.Aonprd,
InnerType = "skill",
Description = "Навыки",
Group = ProcessorGroupEnum.AonprdPreparation
diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Common/SourceProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Common/SourceProcessor.cs
index c20c2c0..570cf42 100644
--- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Common/SourceProcessor.cs
+++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Common/SourceProcessor.cs
@@ -6,9 +6,10 @@ using Pf2eModel.Model.Enum;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Common;
-[Processor(
+[ProcessorCollection(
"aonprd:source",
InnerType = "source",
+ CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Источники",
Group = ProcessorGroupEnum.AonprdPreparation
)]
diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Subclasses/ApparitionProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Subclasses/ApparitionProcessor.cs
index ad7985a..47d2cc9 100644
--- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Subclasses/ApparitionProcessor.cs
+++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Subclasses/ApparitionProcessor.cs
@@ -12,9 +12,10 @@ using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Subclasses;
-[Processor(
+[ProcessorCollection(
"aonprd:apparition",
InnerType = "apparition",
+ CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Apparitions",
Group = ProcessorGroupEnum.AonprdSubclasses
)]
diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/ActionProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/ActionProcessor.cs
index 56174e3..2cbc2fc 100644
--- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/ActionProcessor.cs
+++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/ActionProcessor.cs
@@ -8,9 +8,10 @@ using Pf2eModel.Model.Enum;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Types;
-[Processor(
+[ProcessorCollection(
"aonprd:action",
InnerType = "action",
+ CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Действия",
Group = ProcessorGroupEnum.AonprdEntities
)]
diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/AncestryProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/AncestryProcessor.cs
index a3aa582..3631619 100644
--- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/AncestryProcessor.cs
+++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/AncestryProcessor.cs
@@ -10,9 +10,10 @@ using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Types;
-[Processor(
+[ProcessorCollection(
"aonprd:ancestry",
InnerType = "ancestry",
+ CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Родословные",
Group = ProcessorGroupEnum.AonprdEntities
)]
diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/ArchetypeProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/ArchetypeProcessor.cs
index a97727a..e8d9b62 100644
--- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/ArchetypeProcessor.cs
+++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/ArchetypeProcessor.cs
@@ -9,9 +9,10 @@ using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Types;
-[Processor(
+[ProcessorCollection(
"aonprd:archetype",
InnerType = "archetype",
+ CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Архетипы",
Group = ProcessorGroupEnum.AonprdEntities
)]
diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/BackgroundProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/BackgroundProcessor.cs
index 1b9b7cf..2d857d6 100644
--- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/BackgroundProcessor.cs
+++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/BackgroundProcessor.cs
@@ -11,9 +11,10 @@ using Pf2eModel.Model.Structure.BackgroundRelation;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Types;
-[Processor(
+[ProcessorCollection(
"aonprd:background",
InnerType = "background",
+ CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Предыстории",
Group = ProcessorGroupEnum.AonprdEntities
)]
diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/ClassProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/ClassProcessor.cs
index 2d3a05f..869bcd2 100644
--- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/ClassProcessor.cs
+++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/ClassProcessor.cs
@@ -15,9 +15,10 @@ using static System.Text.RegularExpressions.Regex;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Types;
-[Processor(
+[ProcessorCollection(
"aonprd:class",
InnerType = "class",
+ CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Классы",
Group = ProcessorGroupEnum.AonprdEntities
)]
diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/FeatProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/FeatProcessor.cs
index 05a517f..e1e01da 100644
--- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/FeatProcessor.cs
+++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/FeatProcessor.cs
@@ -10,9 +10,10 @@ using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Types;
-[Processor(
+[ProcessorCollection(
"aonprd:feat",
InnerType = "feat",
+ CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Черты",
Group = ProcessorGroupEnum.AonprdEntities
)]
diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/LanguageProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/LanguageProcessor.cs
index 7d2960a..9ded0f6 100644
--- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/LanguageProcessor.cs
+++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/LanguageProcessor.cs
@@ -9,9 +9,10 @@ using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Types;
-[Processor(
+[ProcessorCollection(
"aonprd:language",
InnerType = "language",
+ CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Языки",
Group = ProcessorGroupEnum.AonprdEntities
)]
diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/RitualProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/RitualProcessor.cs
index 5d96c0e..1a73ccd 100644
--- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/RitualProcessor.cs
+++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/RitualProcessor.cs
@@ -13,9 +13,10 @@ using Pf2eModel.Model.Structure.RitualRelation;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Types;
-[Processor(
+[ProcessorCollection(
"aonprd:ritual",
InnerType = "ritual",
+ CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Ритуалы",
Group = ProcessorGroupEnum.AonprdEntities
)]
diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/Rules/RuleProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/Rules/RuleProcessor.cs
index 5d6737e..6534154 100644
--- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/Rules/RuleProcessor.cs
+++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/Rules/RuleProcessor.cs
@@ -5,6 +5,7 @@ using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers;
using Pf2eDataScrapper.Importers.Implementations.Aonprd.Helpers;
using Pf2eDataScrapper.Processors;
using Pf2eModel;
+using Pf2eModel.Model.Base.Legacy;
using Pf2eModel.Model.Base.Source;
using Pf2eModel.Model.Base.Targets;
using Pf2eModel.Model.Common.I18n;
@@ -13,10 +14,11 @@ using Rule = Pf2eModel.Model.Structure.Rule;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Types.Rules;
-[Processor(
+[ProcessorCollection(
"aonprd:rules",
InnerType = "rules",
- Description = "Правила",
+ CompendiumType = ProcessorCompendiumEnum.Aonprd,
+ Description = "Создание цели внешних ссылок",
Group = ProcessorGroupEnum.AonprdEntities
)]
[ProcessorDependencies(["aonprd:trait", "aonprd:source", "aonprd:rules-toc"])]
diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/SpellProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/SpellProcessor.cs
index d69b536..9a4aaf9 100644
--- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/SpellProcessor.cs
+++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/SpellProcessor.cs
@@ -12,9 +12,10 @@ using Pf2eModel.Model.Structure.SpellRelation;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Types;
-[Processor(
+[ProcessorCollection(
"aonprd:spell",
InnerType = "spell",
+ CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Заклинания",
Group = ProcessorGroupEnum.AonprdEntities
)]
diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/TraitProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/TraitProcessor.cs
index 39a03c1..80e55db 100644
--- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/TraitProcessor.cs
+++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/Processors/Types/TraitProcessor.cs
@@ -9,13 +9,14 @@ using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.Processors.Types;
-[Processor(
+[ProcessorCollection(
"aonprd:trait",
InnerType = "trait",
+ CompendiumType = ProcessorCompendiumEnum.Aonprd,
Description = "Признаки",
Group = ProcessorGroupEnum.AonprdEntities
)]
-[ProcessorDependencies(["aonprd:external", "aonprd:source"])]
+[ProcessorDependencies(["aonprd:legacy"])]
internal class TraitProcessor(
ProcessorInput input,
Pf2eDbContext context,
diff --git a/Pf2eDataScrapper/Importers/Implementations/FoundryEn/Processors/Entities/ActionProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/FoundryEn/Processors/Entities/ActionProcessor.cs
index b07acf0..93777b2 100644
--- a/Pf2eDataScrapper/Importers/Implementations/FoundryEn/Processors/Entities/ActionProcessor.cs
+++ b/Pf2eDataScrapper/Importers/Implementations/FoundryEn/Processors/Entities/ActionProcessor.cs
@@ -7,9 +7,10 @@ using Pf2eModel.Model.Enum;
namespace Pf2eDataScrapper.Importers.Implementations.FoundryEn.Processors.Entities;
-[Processor(
+[ProcessorCollection(
"foundry-en:action",
InnerType = "action",
+ CompendiumType = ProcessorCompendiumEnum.FoundryEn,
Description = "Действия",
Group = ProcessorGroupEnum.FoundryEnEntities,
Active = false
diff --git a/Pf2eDataScrapper/Importers/Implementations/FoundryEn/Processors/Entities/ConditionProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/FoundryEn/Processors/Entities/ConditionProcessor.cs
index c967d8d..1c34cbd 100644
--- a/Pf2eDataScrapper/Importers/Implementations/FoundryEn/Processors/Entities/ConditionProcessor.cs
+++ b/Pf2eDataScrapper/Importers/Implementations/FoundryEn/Processors/Entities/ConditionProcessor.cs
@@ -8,9 +8,10 @@ using Pf2eModel.Model.Structure;
namespace Pf2eDataScrapper.Importers.Implementations.FoundryEn.Processors.Entities;
-[Processor(
+[ProcessorCollection(
"foundry-en:condition",
InnerType = "Item.condition",
+ CompendiumType = ProcessorCompendiumEnum.FoundryEn,
Description = "Состояния",
Group = ProcessorGroupEnum.FoundryEnEntities,
Active = false
diff --git a/Pf2eDataScrapper/Importers/Implementations/FoundryEn/Processors/FoundryEnAbstractProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/FoundryEn/Processors/FoundryEnAbstractProcessor.cs
index 829fc9d..5874ed4 100644
--- a/Pf2eDataScrapper/Importers/Implementations/FoundryEn/Processors/FoundryEnAbstractProcessor.cs
+++ b/Pf2eDataScrapper/Importers/Implementations/FoundryEn/Processors/FoundryEnAbstractProcessor.cs
@@ -7,6 +7,7 @@ using Pf2eDataScrapper.Processors;
using Pf2eModel;
using Pf2eModel.Model;
using Pf2eModel.Model.Base;
+using Pf2eModel.Model.Base.Legacy;
using Pf2eModel.Model.Base.Source;
using Pf2eModel.Model.Base.Targets;
using Pf2eModel.Model.Common.I18n;
diff --git a/Pf2eDataScrapper/Processors/ProcessorExecutor.cs b/Pf2eDataScrapper/Processors/ProcessorExecutor.cs
index 831d350..9d74fa0 100644
--- a/Pf2eDataScrapper/Processors/ProcessorExecutor.cs
+++ b/Pf2eDataScrapper/Processors/ProcessorExecutor.cs
@@ -59,6 +59,10 @@ internal sealed class ProcessorExecutor(
{
Progress.Information(exception.Message);
}
+ catch (SilentSkipItemException)
+ {
+ //
+ }
}
Context.SaveChanges();
};
@@ -85,7 +89,7 @@ internal sealed class ProcessorExecutor(
private IEnumerable IterateProcessors(ProcessorCollectionItem item)
{
- if (!item.IsCollectionProcessor)
+ if (!item.IsCollection)
{
Progress.Local.Reset(1);
Progress.Local.Start(item.Description);
@@ -101,21 +105,20 @@ internal sealed class ProcessorExecutor(
);
}
- AbstractCompendium compendium = item.Group switch
+ AbstractCompendium compendium = item.Compendium switch
{
- ProcessorGroupEnum.AonprdPreparation
- or ProcessorGroupEnum.AonprdEntities
- or ProcessorGroupEnum.AonprdSubclasses => AonprdCompendium,
- ProcessorGroupEnum.FoundryEnPreparation or ProcessorGroupEnum.FoundryEnEntities =>
- FoundryEnCompendium,
+ ProcessorCompendiumEnum.Aonprd => AonprdCompendium,
+ ProcessorCompendiumEnum.FoundryEn => FoundryEnCompendium,
_ => throw new($"Не могу выбрать импортёр для группы {item.Group}"),
};
- Progress.Local.Reset(compendium.Count(item.InnerType!));
+ Progress.Local.Reset(
+ item.InnerType is null ? compendium.Count() : compendium.Count(item.InnerType)
+ );
foreach (
(AbstractCompendiumItem compendiumItem, JObject json) in compendium.Iterate(
- item.InnerType!
+ item.InnerType
)
)
{
@@ -150,3 +153,8 @@ public class LegacyItemException(string name) : SkipItemException(name, "Уст
/// Причина пропуска.
public class SkipItemException(string name, string reason)
: Exception($"Пропускаю элемент '{name}', указана причина: {reason}.") { }
+
+///
+/// Исключение, показывающее, что обработка сущности пропускается без вывода сообщения.
+///
+public class SilentSkipItemException : Exception { }
diff --git a/Pf2eDataScrapper/Processors/ProcessorInput.cs b/Pf2eDataScrapper/Processors/ProcessorInput.cs
index d1351da..e0663b1 100644
--- a/Pf2eDataScrapper/Processors/ProcessorInput.cs
+++ b/Pf2eDataScrapper/Processors/ProcessorInput.cs
@@ -1,4 +1,6 @@
-using Newtonsoft.Json.Linq;
+using System.Security.Cryptography;
+using System.Text;
+using Newtonsoft.Json.Linq;
using NpgsqlTypes;
using Pf2eModel.Model.Common.I18n;
@@ -181,6 +183,18 @@ internal class ProcessorInput(string name, JObject input, IEnumerable
+ /// Создание контрольной суммы объекта.
+ ///
+ /// Использовать шестнадцатеричные цифры в нижнем регистре.
+ /// Контрольная сумма объекта.
+ public string Md5(bool isLowercase = false)
+ {
+ byte[] byteHash = MD5.HashData(Encoding.UTF8.GetBytes(Input.ToString()));
+ string hash = Convert.ToHexString(byteHash).Replace("-", "");
+ return isLowercase ? hash.ToLower() : hash;
+ }
+
///
/// Исключение при неверном значении для перечисленния.
///
diff --git a/Pf2eModel/Migrations/20250514084949_1.Designer.cs b/Pf2eModel/Migrations/20250514102154_1.Designer.cs
similarity index 92%
rename from Pf2eModel/Migrations/20250514084949_1.Designer.cs
rename to Pf2eModel/Migrations/20250514102154_1.Designer.cs
index 66ebb3f..fc193fc 100644
--- a/Pf2eModel/Migrations/20250514084949_1.Designer.cs
+++ b/Pf2eModel/Migrations/20250514102154_1.Designer.cs
@@ -13,7 +13,7 @@ using Pf2eModel.Model.Enum;
namespace Pf2eModel.Migrations
{
[DbContext(typeof(Pf2eDbContext))]
- [Migration("20250514084949_1")]
+ [Migration("20250514102154_1")]
partial class _1
{
///
@@ -86,6 +86,25 @@ namespace Pf2eModel.Migrations
b.ToTable("entity_keyword", (string)null);
});
+ modelBuilder.Entity("EntityLegacyEntity", b =>
+ {
+ b.Property("LegacyEntitiesId")
+ .HasColumnType("integer")
+ .HasColumnName("legacy_entities_id");
+
+ b.Property("RemasterEntitiesId")
+ .HasColumnType("integer")
+ .HasColumnName("remaster_entities_id");
+
+ b.HasKey("LegacyEntitiesId", "RemasterEntitiesId")
+ .HasName("pk_entity_legacy_entity");
+
+ b.HasIndex("RemasterEntitiesId")
+ .HasDatabaseName("ix_entity_legacy_entity_remaster_entities_id");
+
+ b.ToTable("entity_legacy_entity", (string)null);
+ });
+
modelBuilder.Entity("EntityTrait", b =>
{
b.Property("EntitiesId")
@@ -240,6 +259,123 @@ namespace Pf2eModel.Migrations
b.ToTable("keyword_aliases", (string)null);
});
+ modelBuilder.Entity("Pf2eModel.Model.Base.Legacy.LegacyEntity", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("id")
+ .HasColumnOrder(1);
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("jsonb")
+ .HasColumnName("name")
+ .HasColumnOrder(2)
+ .HasComment("Отображаемое имя");
+
+ b.HasKey("Id")
+ .HasName("pk_legacy_entities");
+
+ b.ToTable("legacy_entities", (string)null);
+ });
+
+ modelBuilder.Entity("Pf2eModel.Model.Base.Legacy.LegacyEntityExternal", b =>
+ {
+ b.Property("LegacyEntityId")
+ .HasColumnType("integer")
+ .HasColumnName("legacy_entity_id")
+ .HasColumnOrder(1)
+ .HasComment("Идентификатор сущности");
+
+ b.Property("TargetId")
+ .HasColumnType("integer")
+ .HasColumnName("target_id")
+ .HasColumnOrder(2)
+ .HasComment("Идентификатор цели");
+
+ b.Property("ExternalId")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)")
+ .HasColumnName("external_id")
+ .HasColumnOrder(3);
+
+ b.Property("Md5")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)")
+ .HasColumnName("md5")
+ .HasColumnOrder(5)
+ .HasComment("Контрольная сумма объекта");
+
+ b.Property("Suffix")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)")
+ .HasColumnName("suffix")
+ .HasColumnOrder(4)
+ .HasComment("Часть URL до объекта. Для источников Foundry - полный UUID объекта");
+
+ b.HasKey("LegacyEntityId", "TargetId")
+ .HasName("pk_legacy_entity_externals");
+
+ b.HasIndex("TargetId")
+ .HasDatabaseName("ix_legacy_entity_externals_target_id");
+
+ b.ToTable("legacy_entity_externals", (string)null);
+ });
+
+ modelBuilder.Entity("Pf2eModel.Model.Base.Legacy.LegacyEntitySource", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("id")
+ .HasColumnOrder(0)
+ .HasComment("Идентификатор");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("IsRemaster")
+ .HasColumnType("boolean")
+ .HasColumnName("is_remaster")
+ .HasColumnOrder(4)
+ .HasComment("Флаг, указывающий, что объект является частью ремастера");
+
+ b.Property("LegacyEntityId")
+ .HasColumnType("integer")
+ .HasColumnName("legacy_entity_id")
+ .HasColumnOrder(1)
+ .HasComment("Идентификатор устаревшей сущности");
+
+ b.Property("SourceId")
+ .HasColumnType("integer")
+ .HasColumnName("source_id")
+ .HasColumnOrder(2)
+ .HasComment("Идентификатор источника");
+
+ b.Property("Suffix")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)")
+ .HasColumnName("suffix")
+ .HasColumnOrder(3)
+ .HasComment("Номер страницы, эррата и т.д.");
+
+ b.HasKey("Id")
+ .HasName("pk_legacy_entity_sources");
+
+ b.HasIndex("LegacyEntityId")
+ .HasDatabaseName("ix_legacy_entity_sources_legacy_entity_id");
+
+ b.HasIndex("SourceId")
+ .HasDatabaseName("ix_legacy_entity_sources_source_id");
+
+ b.ToTable("legacy_entity_sources", (string)null);
+ });
+
modelBuilder.Entity("Pf2eModel.Model.Base.Source.EntitySource", b =>
{
b.Property("Id")
@@ -340,6 +476,12 @@ namespace Pf2eModel.Migrations
.HasColumnOrder(2)
.HasComment("Идентификатор цели");
+ b.Property("ExternalId")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)")
+ .HasColumnName("external_id")
+ .HasColumnOrder(3);
+
b.Property("Md5")
.IsRequired()
.HasMaxLength(32)
@@ -1842,6 +1984,23 @@ namespace Pf2eModel.Migrations
.HasConstraintName("fk_entity_keyword_keywords_keywords_id");
});
+ modelBuilder.Entity("EntityLegacyEntity", b =>
+ {
+ b.HasOne("Pf2eModel.Model.Base.Legacy.LegacyEntity", null)
+ .WithMany()
+ .HasForeignKey("LegacyEntitiesId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_entity_legacy_entity_legacy_entities_legacy_entities_id");
+
+ b.HasOne("Pf2eModel.Model.Base.Entity", null)
+ .WithMany()
+ .HasForeignKey("RemasterEntitiesId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_entity_legacy_entity_entities_remaster_entities_id");
+ });
+
modelBuilder.Entity("EntityTrait", b =>
{
b.HasOne("Pf2eModel.Model.Base.Entity", null)
@@ -1871,6 +2030,48 @@ namespace Pf2eModel.Migrations
b.Navigation("Keyword");
});
+ modelBuilder.Entity("Pf2eModel.Model.Base.Legacy.LegacyEntityExternal", b =>
+ {
+ b.HasOne("Pf2eModel.Model.Base.Legacy.LegacyEntity", "LegacyEntity")
+ .WithMany("Externals")
+ .HasForeignKey("LegacyEntityId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_legacy_entity_externals_legacy_entities_legacy_entity_id");
+
+ b.HasOne("Pf2eModel.Model.Base.Targets.ExternalTarget", "Target")
+ .WithMany("LegacyEntityExternals")
+ .HasForeignKey("TargetId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_legacy_entity_externals_external_targets_target_id");
+
+ b.Navigation("LegacyEntity");
+
+ b.Navigation("Target");
+ });
+
+ modelBuilder.Entity("Pf2eModel.Model.Base.Legacy.LegacyEntitySource", b =>
+ {
+ b.HasOne("Pf2eModel.Model.Base.Legacy.LegacyEntity", "LegacyEntity")
+ .WithMany("Sources")
+ .HasForeignKey("LegacyEntityId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_legacy_entity_sources_legacy_entities_legacy_entity_id");
+
+ b.HasOne("Pf2eModel.Model.Base.Source.Source", "Source")
+ .WithMany("LegacyEntitySources")
+ .HasForeignKey("SourceId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_legacy_entity_sources_sources_source_id");
+
+ b.Navigation("LegacyEntity");
+
+ b.Navigation("Source");
+ });
+
modelBuilder.Entity("Pf2eModel.Model.Base.Source.EntitySource", b =>
{
b.HasOne("Pf2eModel.Model.Base.Entity", "Entity")
@@ -2509,14 +2710,25 @@ namespace Pf2eModel.Migrations
b.Navigation("Aliases");
});
+ modelBuilder.Entity("Pf2eModel.Model.Base.Legacy.LegacyEntity", b =>
+ {
+ b.Navigation("Externals");
+
+ b.Navigation("Sources");
+ });
+
modelBuilder.Entity("Pf2eModel.Model.Base.Source.Source", b =>
{
b.Navigation("EntitySources");
+
+ b.Navigation("LegacyEntitySources");
});
modelBuilder.Entity("Pf2eModel.Model.Base.Targets.ExternalTarget", b =>
{
b.Navigation("EntityExternals");
+
+ b.Navigation("LegacyEntityExternals");
});
modelBuilder.Entity("Pf2eModel.Model.Structure.Ancestry", b =>
diff --git a/Pf2eModel/Migrations/20250514084949_1.cs b/Pf2eModel/Migrations/20250514102154_1.cs
similarity index 91%
rename from Pf2eModel/Migrations/20250514084949_1.cs
rename to Pf2eModel/Migrations/20250514102154_1.cs
index dfcb326..fefb752 100644
--- a/Pf2eModel/Migrations/20250514084949_1.cs
+++ b/Pf2eModel/Migrations/20250514102154_1.cs
@@ -101,6 +101,19 @@ namespace Pf2eModel.Migrations
table.PrimaryKey("pk_keywords", x => x.id);
});
+ migrationBuilder.CreateTable(
+ name: "legacy_entities",
+ columns: table => new
+ {
+ id = table.Column(type: "integer", nullable: false)
+ .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
+ name = table.Column(type: "jsonb", nullable: false, comment: "Отображаемое имя")
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("pk_legacy_entities", x => x.id);
+ });
+
migrationBuilder.CreateTable(
name: "sources",
columns: table => new
@@ -399,6 +412,7 @@ namespace Pf2eModel.Migrations
{
entity_id = table.Column(type: "integer", nullable: false, comment: "Идентификатор сущности"),
target_id = table.Column(type: "integer", nullable: false, comment: "Идентификатор цели"),
+ external_id = table.Column(type: "character varying(255)", maxLength: 255, nullable: true),
suffix = table.Column(type: "character varying(255)", maxLength: 255, nullable: false, comment: "Часть URL до объекта. Для источников Foundry - полный UUID объекта"),
md5 = table.Column(type: "character varying(32)", maxLength: 32, nullable: false, comment: "Контрольная сумма объекта")
},
@@ -463,6 +477,57 @@ namespace Pf2eModel.Migrations
onDelete: ReferentialAction.Cascade);
});
+ migrationBuilder.CreateTable(
+ name: "entity_legacy_entity",
+ columns: table => new
+ {
+ legacy_entities_id = table.Column(type: "integer", nullable: false),
+ remaster_entities_id = table.Column(type: "integer", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("pk_entity_legacy_entity", x => new { x.legacy_entities_id, x.remaster_entities_id });
+ table.ForeignKey(
+ name: "fk_entity_legacy_entity_entities_remaster_entities_id",
+ column: x => x.remaster_entities_id,
+ principalTable: "entities",
+ principalColumn: "id",
+ onDelete: ReferentialAction.Cascade);
+ table.ForeignKey(
+ name: "fk_entity_legacy_entity_legacy_entities_legacy_entities_id",
+ column: x => x.legacy_entities_id,
+ principalTable: "legacy_entities",
+ principalColumn: "id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "legacy_entity_externals",
+ columns: table => new
+ {
+ legacy_entity_id = table.Column(type: "integer", nullable: false, comment: "Идентификатор сущности"),
+ target_id = table.Column(type: "integer", nullable: false, comment: "Идентификатор цели"),
+ external_id = table.Column(type: "character varying(255)", maxLength: 255, nullable: true),
+ suffix = table.Column(type: "character varying(255)", maxLength: 255, nullable: false, comment: "Часть URL до объекта. Для источников Foundry - полный UUID объекта"),
+ md5 = table.Column(type: "character varying(32)", maxLength: 32, nullable: false, comment: "Контрольная сумма объекта")
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("pk_legacy_entity_externals", x => new { x.legacy_entity_id, x.target_id });
+ table.ForeignKey(
+ name: "fk_legacy_entity_externals_external_targets_target_id",
+ column: x => x.target_id,
+ principalTable: "external_targets",
+ principalColumn: "id",
+ onDelete: ReferentialAction.Cascade);
+ table.ForeignKey(
+ name: "fk_legacy_entity_externals_legacy_entities_legacy_entity_id",
+ column: x => x.legacy_entity_id,
+ principalTable: "legacy_entities",
+ principalColumn: "id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
migrationBuilder.CreateTable(
name: "entity_sources",
columns: table => new
@@ -491,6 +556,34 @@ namespace Pf2eModel.Migrations
onDelete: ReferentialAction.Cascade);
});
+ migrationBuilder.CreateTable(
+ name: "legacy_entity_sources",
+ columns: table => new
+ {
+ id = table.Column(type: "integer", nullable: false, comment: "Идентификатор")
+ .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
+ legacy_entity_id = table.Column(type: "integer", nullable: false, comment: "Идентификатор устаревшей сущности"),
+ source_id = table.Column(type: "integer", nullable: false, comment: "Идентификатор источника"),
+ suffix = table.Column(type: "character varying(255)", maxLength: 255, nullable: false, comment: "Номер страницы, эррата и т.д."),
+ is_remaster = table.Column(type: "boolean", nullable: false, comment: "Флаг, указывающий, что объект является частью ремастера")
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("pk_legacy_entity_sources", x => x.id);
+ table.ForeignKey(
+ name: "fk_legacy_entity_sources_legacy_entities_legacy_entity_id",
+ column: x => x.legacy_entity_id,
+ principalTable: "legacy_entities",
+ principalColumn: "id",
+ onDelete: ReferentialAction.Cascade);
+ table.ForeignKey(
+ name: "fk_legacy_entity_sources_sources_source_id",
+ column: x => x.source_id,
+ principalTable: "sources",
+ principalColumn: "id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
migrationBuilder.CreateTable(
name: "ancestry_boosts",
columns: table => new
@@ -1260,6 +1353,11 @@ namespace Pf2eModel.Migrations
table: "entity_keyword",
column: "keywords_id");
+ migrationBuilder.CreateIndex(
+ name: "ix_entity_legacy_entity_remaster_entities_id",
+ table: "entity_legacy_entity",
+ column: "remaster_entities_id");
+
migrationBuilder.CreateIndex(
name: "ix_entity_sources_entity_id",
table: "entity_sources",
@@ -1295,6 +1393,21 @@ namespace Pf2eModel.Migrations
table: "languages",
column: "entity_id");
+ migrationBuilder.CreateIndex(
+ name: "ix_legacy_entity_externals_target_id",
+ table: "legacy_entity_externals",
+ column: "target_id");
+
+ migrationBuilder.CreateIndex(
+ name: "ix_legacy_entity_sources_legacy_entity_id",
+ table: "legacy_entity_sources",
+ column: "legacy_entity_id");
+
+ migrationBuilder.CreateIndex(
+ name: "ix_legacy_entity_sources_source_id",
+ table: "legacy_entity_sources",
+ column: "source_id");
+
migrationBuilder.CreateIndex(
name: "ix_ritual_checks_ritual_id",
table: "ritual_checks",
@@ -1411,6 +1524,9 @@ namespace Pf2eModel.Migrations
migrationBuilder.DropTable(
name: "entity_keyword");
+ migrationBuilder.DropTable(
+ name: "entity_legacy_entity");
+
migrationBuilder.DropTable(
name: "entity_sources");
@@ -1429,6 +1545,12 @@ namespace Pf2eModel.Migrations
migrationBuilder.DropTable(
name: "keyword_aliases");
+ migrationBuilder.DropTable(
+ name: "legacy_entity_externals");
+
+ migrationBuilder.DropTable(
+ name: "legacy_entity_sources");
+
migrationBuilder.DropTable(
name: "ritual_areas");
@@ -1465,12 +1587,6 @@ namespace Pf2eModel.Migrations
migrationBuilder.DropTable(
name: "deities");
- migrationBuilder.DropTable(
- name: "external_targets");
-
- migrationBuilder.DropTable(
- name: "sources");
-
migrationBuilder.DropTable(
name: "traits");
@@ -1480,6 +1596,15 @@ namespace Pf2eModel.Migrations
migrationBuilder.DropTable(
name: "keywords");
+ migrationBuilder.DropTable(
+ name: "external_targets");
+
+ migrationBuilder.DropTable(
+ name: "legacy_entities");
+
+ migrationBuilder.DropTable(
+ name: "sources");
+
migrationBuilder.DropTable(
name: "rituals");
diff --git a/Pf2eModel/Migrations/Pf2eDbContextModelSnapshot.cs b/Pf2eModel/Migrations/Pf2eDbContextModelSnapshot.cs
index 1dcf56e..77fe09f 100644
--- a/Pf2eModel/Migrations/Pf2eDbContextModelSnapshot.cs
+++ b/Pf2eModel/Migrations/Pf2eDbContextModelSnapshot.cs
@@ -83,6 +83,25 @@ namespace Pf2eModel.Migrations
b.ToTable("entity_keyword", (string)null);
});
+ modelBuilder.Entity("EntityLegacyEntity", b =>
+ {
+ b.Property("LegacyEntitiesId")
+ .HasColumnType("integer")
+ .HasColumnName("legacy_entities_id");
+
+ b.Property("RemasterEntitiesId")
+ .HasColumnType("integer")
+ .HasColumnName("remaster_entities_id");
+
+ b.HasKey("LegacyEntitiesId", "RemasterEntitiesId")
+ .HasName("pk_entity_legacy_entity");
+
+ b.HasIndex("RemasterEntitiesId")
+ .HasDatabaseName("ix_entity_legacy_entity_remaster_entities_id");
+
+ b.ToTable("entity_legacy_entity", (string)null);
+ });
+
modelBuilder.Entity("EntityTrait", b =>
{
b.Property("EntitiesId")
@@ -237,6 +256,123 @@ namespace Pf2eModel.Migrations
b.ToTable("keyword_aliases", (string)null);
});
+ modelBuilder.Entity("Pf2eModel.Model.Base.Legacy.LegacyEntity", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("id")
+ .HasColumnOrder(1);
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("jsonb")
+ .HasColumnName("name")
+ .HasColumnOrder(2)
+ .HasComment("Отображаемое имя");
+
+ b.HasKey("Id")
+ .HasName("pk_legacy_entities");
+
+ b.ToTable("legacy_entities", (string)null);
+ });
+
+ modelBuilder.Entity("Pf2eModel.Model.Base.Legacy.LegacyEntityExternal", b =>
+ {
+ b.Property("LegacyEntityId")
+ .HasColumnType("integer")
+ .HasColumnName("legacy_entity_id")
+ .HasColumnOrder(1)
+ .HasComment("Идентификатор сущности");
+
+ b.Property("TargetId")
+ .HasColumnType("integer")
+ .HasColumnName("target_id")
+ .HasColumnOrder(2)
+ .HasComment("Идентификатор цели");
+
+ b.Property("ExternalId")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)")
+ .HasColumnName("external_id")
+ .HasColumnOrder(3);
+
+ b.Property("Md5")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)")
+ .HasColumnName("md5")
+ .HasColumnOrder(5)
+ .HasComment("Контрольная сумма объекта");
+
+ b.Property("Suffix")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)")
+ .HasColumnName("suffix")
+ .HasColumnOrder(4)
+ .HasComment("Часть URL до объекта. Для источников Foundry - полный UUID объекта");
+
+ b.HasKey("LegacyEntityId", "TargetId")
+ .HasName("pk_legacy_entity_externals");
+
+ b.HasIndex("TargetId")
+ .HasDatabaseName("ix_legacy_entity_externals_target_id");
+
+ b.ToTable("legacy_entity_externals", (string)null);
+ });
+
+ modelBuilder.Entity("Pf2eModel.Model.Base.Legacy.LegacyEntitySource", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("id")
+ .HasColumnOrder(0)
+ .HasComment("Идентификатор");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("IsRemaster")
+ .HasColumnType("boolean")
+ .HasColumnName("is_remaster")
+ .HasColumnOrder(4)
+ .HasComment("Флаг, указывающий, что объект является частью ремастера");
+
+ b.Property("LegacyEntityId")
+ .HasColumnType("integer")
+ .HasColumnName("legacy_entity_id")
+ .HasColumnOrder(1)
+ .HasComment("Идентификатор устаревшей сущности");
+
+ b.Property("SourceId")
+ .HasColumnType("integer")
+ .HasColumnName("source_id")
+ .HasColumnOrder(2)
+ .HasComment("Идентификатор источника");
+
+ b.Property("Suffix")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)")
+ .HasColumnName("suffix")
+ .HasColumnOrder(3)
+ .HasComment("Номер страницы, эррата и т.д.");
+
+ b.HasKey("Id")
+ .HasName("pk_legacy_entity_sources");
+
+ b.HasIndex("LegacyEntityId")
+ .HasDatabaseName("ix_legacy_entity_sources_legacy_entity_id");
+
+ b.HasIndex("SourceId")
+ .HasDatabaseName("ix_legacy_entity_sources_source_id");
+
+ b.ToTable("legacy_entity_sources", (string)null);
+ });
+
modelBuilder.Entity("Pf2eModel.Model.Base.Source.EntitySource", b =>
{
b.Property("Id")
@@ -337,6 +473,12 @@ namespace Pf2eModel.Migrations
.HasColumnOrder(2)
.HasComment("Идентификатор цели");
+ b.Property("ExternalId")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)")
+ .HasColumnName("external_id")
+ .HasColumnOrder(3);
+
b.Property("Md5")
.IsRequired()
.HasMaxLength(32)
@@ -1839,6 +1981,23 @@ namespace Pf2eModel.Migrations
.HasConstraintName("fk_entity_keyword_keywords_keywords_id");
});
+ modelBuilder.Entity("EntityLegacyEntity", b =>
+ {
+ b.HasOne("Pf2eModel.Model.Base.Legacy.LegacyEntity", null)
+ .WithMany()
+ .HasForeignKey("LegacyEntitiesId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_entity_legacy_entity_legacy_entities_legacy_entities_id");
+
+ b.HasOne("Pf2eModel.Model.Base.Entity", null)
+ .WithMany()
+ .HasForeignKey("RemasterEntitiesId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_entity_legacy_entity_entities_remaster_entities_id");
+ });
+
modelBuilder.Entity("EntityTrait", b =>
{
b.HasOne("Pf2eModel.Model.Base.Entity", null)
@@ -1868,6 +2027,48 @@ namespace Pf2eModel.Migrations
b.Navigation("Keyword");
});
+ modelBuilder.Entity("Pf2eModel.Model.Base.Legacy.LegacyEntityExternal", b =>
+ {
+ b.HasOne("Pf2eModel.Model.Base.Legacy.LegacyEntity", "LegacyEntity")
+ .WithMany("Externals")
+ .HasForeignKey("LegacyEntityId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_legacy_entity_externals_legacy_entities_legacy_entity_id");
+
+ b.HasOne("Pf2eModel.Model.Base.Targets.ExternalTarget", "Target")
+ .WithMany("LegacyEntityExternals")
+ .HasForeignKey("TargetId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_legacy_entity_externals_external_targets_target_id");
+
+ b.Navigation("LegacyEntity");
+
+ b.Navigation("Target");
+ });
+
+ modelBuilder.Entity("Pf2eModel.Model.Base.Legacy.LegacyEntitySource", b =>
+ {
+ b.HasOne("Pf2eModel.Model.Base.Legacy.LegacyEntity", "LegacyEntity")
+ .WithMany("Sources")
+ .HasForeignKey("LegacyEntityId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_legacy_entity_sources_legacy_entities_legacy_entity_id");
+
+ b.HasOne("Pf2eModel.Model.Base.Source.Source", "Source")
+ .WithMany("LegacyEntitySources")
+ .HasForeignKey("SourceId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_legacy_entity_sources_sources_source_id");
+
+ b.Navigation("LegacyEntity");
+
+ b.Navigation("Source");
+ });
+
modelBuilder.Entity("Pf2eModel.Model.Base.Source.EntitySource", b =>
{
b.HasOne("Pf2eModel.Model.Base.Entity", "Entity")
@@ -2506,14 +2707,25 @@ namespace Pf2eModel.Migrations
b.Navigation("Aliases");
});
+ modelBuilder.Entity("Pf2eModel.Model.Base.Legacy.LegacyEntity", b =>
+ {
+ b.Navigation("Externals");
+
+ b.Navigation("Sources");
+ });
+
modelBuilder.Entity("Pf2eModel.Model.Base.Source.Source", b =>
{
b.Navigation("EntitySources");
+
+ b.Navigation("LegacyEntitySources");
});
modelBuilder.Entity("Pf2eModel.Model.Base.Targets.ExternalTarget", b =>
{
b.Navigation("EntityExternals");
+
+ b.Navigation("LegacyEntityExternals");
});
modelBuilder.Entity("Pf2eModel.Model.Structure.Ancestry", b =>
diff --git a/Pf2eModel/Model/Base/Entity.cs b/Pf2eModel/Model/Base/Entity.cs
index 429e1c8..89cff39 100644
--- a/Pf2eModel/Model/Base/Entity.cs
+++ b/Pf2eModel/Model/Base/Entity.cs
@@ -2,6 +2,7 @@
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Pf2eModel.Model.Base.Keywords;
+using Pf2eModel.Model.Base.Legacy;
using Pf2eModel.Model.Base.Source;
using Pf2eModel.Model.Base.Targets;
using Pf2eModel.Model.Common.I18n;
@@ -99,4 +100,9 @@ public class Entity
/// Коллекция внешних ссылок, связанных с сущностью.
///
public ICollection Externals { get; set; } = [];
+
+ ///
+ /// Коллекция устаревших сущностей.
+ ///
+ public IEnumerable LegacyEntities { get; set; } = [];
}
diff --git a/Pf2eModel/Model/Base/Legacy/LegacyEntity.cs b/Pf2eModel/Model/Base/Legacy/LegacyEntity.cs
new file mode 100644
index 0000000..af1251a
--- /dev/null
+++ b/Pf2eModel/Model/Base/Legacy/LegacyEntity.cs
@@ -0,0 +1,42 @@
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Microsoft.EntityFrameworkCore;
+using Pf2eModel.Model.Common.I18n;
+
+namespace Pf2eModel.Model.Base.Legacy;
+
+///
+/// Информация об устаревшей сущности
+///
+public class LegacyEntity
+{
+ ///
+ /// Идентификатор сущности.
+ ///
+ [Key]
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ [Column(Order = 1)]
+ public int Id { get; set; }
+
+ ///
+ /// Отображаемое имя сущности.
+ ///
+ [Column(Order = 2)]
+ [Comment("Отображаемое имя")]
+ public LocaleString Name { get; set; } = [];
+
+ ///
+ /// Коллекция обновлённых сущностей.
+ ///
+ public ICollection RemasterEntities { get; set; } = [];
+
+ ///
+ /// Коллекция источников, связанных с сущностью.
+ ///
+ public ICollection Sources { get; set; } = [];
+
+ ///
+ /// Коллекция внешних ссылок, связанных с сущностью.
+ ///
+ public ICollection Externals { get; set; } = [];
+}
diff --git a/Pf2eModel/Model/Base/Legacy/LegacyEntityExternal.cs b/Pf2eModel/Model/Base/Legacy/LegacyEntityExternal.cs
new file mode 100644
index 0000000..962635d
--- /dev/null
+++ b/Pf2eModel/Model/Base/Legacy/LegacyEntityExternal.cs
@@ -0,0 +1,58 @@
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Microsoft.EntityFrameworkCore;
+using Pf2eModel.Model.Base.Targets;
+
+namespace Pf2eModel.Model.Base.Legacy;
+
+///
+/// Внешняя ссылка сущности.
+///
+[PrimaryKey(nameof(LegacyEntityId), nameof(TargetId))]
+public class LegacyEntityExternal
+{
+ ///
+ /// Идентификатор сущности.
+ ///
+ [Column(Order = 1)]
+ [Comment("Идентификатор сущности")]
+ public int LegacyEntityId { get; set; }
+
+ ///
+ /// Идентификатор цели.
+ ///
+ [Column(Order = 2)]
+ [Comment("Идентификатор цели")]
+ public int TargetId { get; set; }
+
+ [Column(Order = 3)]
+ [MaxLength(255)]
+ public string? ExternalId { get; set; } = null;
+
+ ///
+ /// Конечная часть URL внешней ссылки.
+ /// Для источников Foundry - полный UUID объекта.
+ ///
+ [Column(Order = 4)]
+ [MaxLength(255)]
+ [Comment("Часть URL до объекта. Для источников Foundry - полный UUID объекта")]
+ public string Suffix { get; set; } = "";
+
+ ///
+ /// Контрольная сумма объекта.
+ ///
+ [Column(Order = 5)]
+ [MaxLength(32)]
+ [Comment("Контрольная сумма объекта")]
+ public string Md5 { get; set; } = null!;
+
+ ///
+ /// Сущность, которая ссылается на внешнюю ссылку.
+ ///
+ public LegacyEntity LegacyEntity { get; set; } = null!;
+
+ ///
+ /// Цель внешней ссылки.
+ ///
+ public ExternalTarget Target { get; set; } = null!;
+}
diff --git a/Pf2eModel/Model/Base/Legacy/LegacyEntitySource.cs b/Pf2eModel/Model/Base/Legacy/LegacyEntitySource.cs
new file mode 100644
index 0000000..a3d959d
--- /dev/null
+++ b/Pf2eModel/Model/Base/Legacy/LegacyEntitySource.cs
@@ -0,0 +1,59 @@
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Microsoft.EntityFrameworkCore;
+
+namespace Pf2eModel.Model.Base.Legacy;
+
+///
+/// Связь между сущностью и источником.
+///
+public class LegacyEntitySource
+{
+ ///
+ /// Идентификатор связи.
+ ///
+ [Key]
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ [Column(Order = 0)]
+ [Comment("Идентификатор")]
+ public int Id { get; set; }
+
+ ///
+ /// Идентификатор устаревшей сущности.
+ ///
+ [Column(Order = 1)]
+ [Comment("Идентификатор устаревшей сущности")]
+ public int LegacyEntityId { get; set; }
+
+ ///
+ /// Идентификатор источника.
+ ///
+ [Column(Order = 2)]
+ [Comment("Идентификатор источника")]
+ public int SourceId { get; set; }
+
+ ///
+ /// Дополнительная информация о источнике, например, номер страницы, номер тома и т.д.
+ ///
+ [Column(Order = 3)]
+ [MaxLength(255)]
+ [Comment("Номер страницы, эррата и т.д.")]
+ public string Suffix { get; set; } = null!;
+
+ ///
+ /// Флаг, указывающий, что объект является частью ремастера.
+ ///
+ [Column(Order = 4)]
+ [Comment("Флаг, указывающий, что объект является частью ремастера")]
+ public bool IsRemaster { get; set; }
+
+ ///
+ /// Сущность, к которой относится источник.
+ ///
+ public LegacyEntity LegacyEntity { get; set; } = null!;
+
+ ///
+ /// Источник, к которому относится сущность.
+ ///
+ public Source.Source Source { get; set; } = null!;
+}
diff --git a/Pf2eModel/Model/Base/Source/Source.cs b/Pf2eModel/Model/Base/Source/Source.cs
index 7f098f1..6a88179 100644
--- a/Pf2eModel/Model/Base/Source/Source.cs
+++ b/Pf2eModel/Model/Base/Source/Source.cs
@@ -1,6 +1,7 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
+using Pf2eModel.Model.Base.Legacy;
using Pf2eModel.Model.Enum;
namespace Pf2eModel.Model.Base.Source;
@@ -45,4 +46,9 @@ public class Source
/// Связанные сущности.
///
public ICollection EntitySources { get; set; } = [];
+
+ ///
+ /// Связанные сущности.
+ ///
+ public ICollection LegacyEntitySources { get; set; } = [];
}
diff --git a/Pf2eModel/Model/Base/Targets/EntityExternal.cs b/Pf2eModel/Model/Base/Targets/EntityExternal.cs
index a662e50..56f4951 100644
--- a/Pf2eModel/Model/Base/Targets/EntityExternal.cs
+++ b/Pf2eModel/Model/Base/Targets/EntityExternal.cs
@@ -24,11 +24,9 @@ public class EntityExternal
[Comment("Идентификатор цели")]
public int TargetId { get; set; }
- /*
[Column(Order = 3)]
[MaxLength(255)]
- public string ExternalId { get; set; } = "";
- */
+ public string? ExternalId { get; set; } = null;
///
/// Конечная часть URL внешней ссылки.
diff --git a/Pf2eModel/Model/Base/Targets/ExternalTarget.cs b/Pf2eModel/Model/Base/Targets/ExternalTarget.cs
index 44b74bb..6602b5b 100644
--- a/Pf2eModel/Model/Base/Targets/ExternalTarget.cs
+++ b/Pf2eModel/Model/Base/Targets/ExternalTarget.cs
@@ -1,6 +1,7 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
+using Pf2eModel.Model.Base.Legacy;
namespace Pf2eModel.Model.Base.Targets;
@@ -46,4 +47,9 @@ public class ExternalTarget
/// Коллекция сущностей, которые ссылаются на эту цель.
///
public ICollection EntityExternals { get; set; } = [];
+
+ ///
+ /// Коллекция устаревших сущностей, которые ссылаются на эту цель.
+ ///
+ public ICollection LegacyEntityExternals { get; set; } = [];
}
diff --git a/Pf2eModel/Pf2eDbContext.cs b/Pf2eModel/Pf2eDbContext.cs
index 4bcf998..2ecf8c7 100644
--- a/Pf2eModel/Pf2eDbContext.cs
+++ b/Pf2eModel/Pf2eDbContext.cs
@@ -3,6 +3,7 @@ using dotenv.net.Utilities;
using Microsoft.EntityFrameworkCore;
using Pf2eModel.Model.Base;
using Pf2eModel.Model.Base.Keywords;
+using Pf2eModel.Model.Base.Legacy;
using Pf2eModel.Model.Base.Source;
using Pf2eModel.Model.Base.Targets;
using Pf2eModel.Model.Common;
@@ -97,6 +98,12 @@ public partial class Pf2eDbContext : DbContext
public virtual DbSet Languages { get; set; }
+ public virtual DbSet LegacyEntities { get; set; }
+
+ public virtual DbSet LegacyEntityExternals { get; set; }
+
+ public virtual DbSet LegacyEntitySources { get; set; }
+
public virtual DbSet Rituals { get; set; }
public virtual DbSet RitualAreas { get; set; }
diff --git a/Pf2eModel/Pf2eDbContextModel.cs b/Pf2eModel/Pf2eDbContextModel.cs
index 56a8917..94afdf3 100644
--- a/Pf2eModel/Pf2eDbContextModel.cs
+++ b/Pf2eModel/Pf2eDbContextModel.cs
@@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Pf2eModel.Model;
using Pf2eModel.Model.Base;
using Pf2eModel.Model.Base.Keywords;
+using Pf2eModel.Model.Base.Legacy;
using Pf2eModel.Model.Base.Source;
using Pf2eModel.Model.Base.Targets;
using Pf2eModel.Model.Common;
@@ -144,6 +145,28 @@ public partial class Pf2eDbContext
.OnDelete(DeleteBehavior.Cascade);
});
+ // LegacyEntity
+ modelBuilder.Entity(entity =>
+ {
+ BindLocaleString(entity, l => l.Name);
+
+ entity.HasMany(l => l.RemasterEntities).WithMany(e => e.LegacyEntities);
+
+ // LegacyEntitySource
+ entity
+ .HasMany(e => e.Sources)
+ .WithOne(e => e.LegacyEntity)
+ .HasForeignKey(e => e.LegacyEntityId)
+ .OnDelete(DeleteBehavior.Cascade);
+
+ // LegacyEntityExternal
+ entity
+ .HasMany(e => e.Externals)
+ .WithOne(e => e.LegacyEntity)
+ .HasForeignKey(e => e.LegacyEntityId)
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
// Source
modelBuilder.Entity(entity =>
{
@@ -155,6 +178,12 @@ public partial class Pf2eDbContext
.HasForeignKey(e => e.SourceId)
.OnDelete(DeleteBehavior.Cascade);
+ entity
+ .HasMany(s => s.LegacyEntitySources)
+ .WithOne(e => e.Source)
+ .HasForeignKey(e => e.SourceId)
+ .OnDelete(DeleteBehavior.Cascade);
+
// + EntitySource
});
modelBuilder.Entity(entity => { });
@@ -167,6 +196,12 @@ public partial class Pf2eDbContext
.WithOne(e => e.Target)
.HasForeignKey(e => e.TargetId)
.OnDelete(DeleteBehavior.Cascade);
+
+ entity
+ .HasMany(t => t.LegacyEntityExternals)
+ .WithOne(e => e.Target)
+ .HasForeignKey(e => e.TargetId)
+ .OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity(entity => { });