I'm using EF Core and implementing inheritance in a table-per-hierarchy (TPH) approach. I've found that once I added the hierarchy objects to my data model the migration is adding a foreign key for the base table and what appears to be a duplicate that the descendant classes are using.
Relationship is from Transaction (parent) to TransactionItem (child).
Object hierarchy:
Using Fluent API to define the relationship between Transaction and TransactionItem using the TransactionItem.TransactionId field as the foreign key:
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");
});
The migration Up method is creating the base class table (TPH) with TransactionId and TransactionId2 columns, each relating to the same parent table:
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);
});
When I look at the ModelSnapshot.cs code I see the following relationships specific to the objects in the inheritance model establishing a foreign key for the derived class entities using the TransactionId2 column.
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");
});
I'm obviously missing something but can't figure out what it is. I want to be able to use inheritance hierarchy with TPH approach and a single foreign key field from the base class table (TransactionItem) to the parent (Transaction). I appreciate any help that you can provide.
Thanks!
As pointed out by Ivan Stoev, I created this problem by having the parent object (Transaction in this case) implement read-only convenience properties for lists of my derived class types.
Because Transaction declared properties like below, the ef migration was creating new relationships/foreign keys for them.
public List<Check> Checks
{
get
{
return this.TransactionItems?.OfType<Check>().ToList();
}
}
I've since implemented these as methods so as not to conflict with the conventions of ef.
Many thanks to Ivan Stoev for prompting this discovery!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With