Производные типы, генерирующие собственное поле ForeignKey в коде, сначала с использованием TPH
Я использую EF Core и реализую наследование в подходе таблицы на иерархию (TPH). Я обнаружил, что после добавления объектов иерархии в мою модель данных миграция добавляет внешний ключ для базовой таблицы и что-то похожее на дубликат, который используют классы-потомки.
Отношения от Transaction
(родитель) в TransactionItem
(Ребенок).
Иерархия объектов:
- TransactionItem (Базовый класс)
- прикрепление
- Проверьте
- Конверт
- огрызок
Использование Fluent API для определения отношений между Transaction
а также TransactionItem
с использованием TransactionItem.TransactionId
поле в качестве внешнего ключа:
modelBuilder.Entity<TransactionItem>(t =>
{
t.ToTable("TransactionItem");
t.HasKey(a => a.Id);
t.Property(a => a.ConcurrencyStamp)
.IsRequired()
.IsConcurrencyToken();
t.Property(a => a.Created)
.IsRequired();
t.HasOne(a => a.Transaction)
.WithMany(a => a.TransactionItems)
.HasForeignKey(a => a.TransactionId)
.OnDelete(DeleteBehavior.Restrict);
t.HasDiscriminator<string>("ItemType")
.HasValue<Check>("Check")
.HasValue<Stub>("Stub")
.HasValue<Envelope>("Envelope")
.HasValue<Attachment>("Attachment");
});
Метод миграции Up создает таблицу базовых классов (TPH) с TransactionId
а также TransactionId2
столбцы, каждый из которых относится к одной родительской таблице:
migrationBuilder.CreateTable(
name: "TransactionItem",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
ConcurrencyStamp = table.Column<string>(nullable: false),
Created = table.Column<DateTimeOffset>(nullable: false),
ItemType = table.Column<string>(nullable: false),
PageName = table.Column<string>(nullable: true),
Sequence = table.Column<int>(nullable: false),
TransactionId = table.Column<int>(nullable: false),
TransactionId2 = table.Column<int>(nullable: true),
CheckType = table.Column<string>(nullable: true),
Micr = table.Column<string>(nullable: true),
MicrValid = table.Column<bool>(nullable: true),
OcrId = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_TransactionItem", x => x.Id);
table.ForeignKey(
name: "FK_TransactionItem_Transaction_TransactionId",
column: x => x.TransactionId,
principalTable: "Transaction",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_TransactionItem_Transaction_TransactionId2",
column: x => x.TransactionId2,
principalTable: "Transaction",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
Когда я смотрю на код ModelSnapshot.cs, я вижу следующие отношения, специфичные для объектов в модели наследования, устанавливающих внешний ключ для объектов производного класса, используя TransactionId2
колонка.
modelBuilder.Entity("SampleProject.Data.DataModels.TransactionItem", b =>
{
b.HasOne("SampleProject.Data.DataModels.Transaction", "Transaction")
.WithMany("TransactionItems")
.HasForeignKey("TransactionId");
});
modelBuilder.Entity("SampleProject.Data.DataModels.Attachment", b =>
{
b.HasOne("SampleProject.Data.DataModels.Transaction")
.WithMany("Attachments")
.HasForeignKey("TransactionId2");
});
modelBuilder.Entity("SampleProject.Data.DataModels.Check", b =>
{
b.HasOne("SampleProject.Data.DataModels.Transaction")
.WithMany("Checks")
.HasForeignKey("TransactionId2");
});
modelBuilder.Entity("SampleProject.Data.DataModels.Envelope", b =>
{
b.HasOne("SampleProject.Data.DataModels.Transaction")
.WithMany("Envelopes")
.HasForeignKey("TransactionId2");
});
modelBuilder.Entity("SampleProject.Data.DataModels.Stub", b =>
{
b.HasOne("SampleProject.Data.DataModels.Transaction")
.WithMany("Stubs")
.HasForeignKey("TransactionId2");
});
Я явно что-то упускаю, но не могу понять, что это такое. Я хочу иметь возможность использовать иерархию наследования с подходом TPH и одно поле внешнего ключа из таблицы базового класса (TransactionItem
) к родителю (Transaction
). Я ценю любую помощь, которую вы можете оказать.
Спасибо!
1 ответ
Как указал Иван Стоев, я создал эту проблему, имея родительский объект (Transaction
в этом случае) реализовать удобные свойства только для чтения для списков моих производных типов классов.
Так как Transaction
объявленные свойства, как показано ниже, efigration создала для них новые отношения / внешние ключи.
public List<Check> Checks
{
get
{
return this.TransactionItems?.OfType<Check>().ToList();
}
}
С тех пор я реализовал их как методы, чтобы не противоречить соглашениям ef.
Большое спасибо Ивану Стоеву за подсказку этого открытия!