将复合键与 Entity Framework 核心一起使用并将其中的一部分用作外键
Using Composite Keys with Entity Framework Core and Using part of them as foreign keys
我正在尝试制作几个具有复合键的 table(如下例)。我已经能够使用代码优先方法实现这一点,但我希望这些复合键能够级联到子 tables。这个想法是,每个子实体都将具有与其父实体相同的复合键,再加上一列。
| PurchaseOrder |
| ------------- |
| Company (PK) |
| PONum (PK) |
| PuchaseOrderLine |
| ---------------- |
| Company (PK/FK) |
| PONum (PK/FK) |
| POLine (PK) |
从技术上讲,PurchaseOrder table 会做与公司类似的事情 table 但这对我来说不太重要,我想如果我能够弄清楚 POLine 到 PO 的连接我也会想出那个。
这是我目前的尝试:
// OnModelCreating method in my class that inherits IdentityDbContext<IdentityUser>
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Company>()
.DefaultConfigure(c => new { c.Name });
builder.Entity<PurchaseOrder>()
.DefaultConfigure(p => new { p.Company, p.PONum })
.HasMany(e => e.PurchaseOrderLines)
.WithOne(e => e.PurchaseOrder);
builder.Entity<PurchaseOrderLine>()
.DefaultConfigure(p => new { p.Company, p.PONum, p.LineNum })
.HasOne(e => e.PurchaseOrder)
.WithMany(e => e.PurchaseOrderLines);
}
// My DefaultConfigure extension method
public static EntityTypeBuilder<T> DefaultConfigure<T>(this EntityTypeBuilder<T> builder, Expression<Func<T, object>> keyExpression)
where T : AuditableEntity
{
// Rename table to the types name
builder.ToTable(typeof(T).Name.ToLower());
// Configure the keys they passed in
builder.HasKey(keyExpression);
// Configure the default alternate key
builder.HasAlternateKey(i => new { i.RowId });
// Give the builder back
return builder;
}
当我 运行 此迁移并更新我的数据库时,这是我的 PurchaseOrderLine table 上的内容:
| COLUMN_NAME | CONSTRAINT_NAME |
| -------------------- | -------------------------------------------------------------------------- |
| RowId | AK_PurchaseOrderLine_RowId |
| PurchaseOrderCompany | FK_PurchaseOrderLine_PurchaseOrder_PurchaseOrderCompany_PurchaseOrderPONum |
| PurchaseOrderPONum | FK_PurchaseOrderLine_PurchaseOrder_PurchaseOrderCompany_PurchaseOrderPONum |
| Company | PK_PurchaseOrderLine |
| LineNum | PK_PurchaseOrderLine |
| PONum | PK_PurchaseOrderLine |
EFCore 刚刚使用默认命名方案添加了两个新列,并没有使用我已有的列。有没有办法让 efcore 使用代码优先方法来做这样的事情?
不要这样做。使用自动递增的主键。在父表中查找您需要的值。
复合主键只会让外键变得很麻烦。它们的效率也较低,如果广泛用作外键,通常会使用更多 space。
所以,PurchaseOrder
应该有一个 PurchaseOrderId
。那么PurchaseOrderLine
应该参考PurchaseOrderId
,需要的时候去查一下公司等相关资料
回答你的具体问题
Is there anyway to get efcore to do something like this using the code first approach?
当然有。只是 conventional foreign key names 显然不起作用,因此您必须明确配置 FK 属性(通过 HasForeignKey
fluent API)。
例如或者
builder.Entity<PurchaseOrderLine>()
.DefaultConfigure(p => new { p.Company, p.PONum, p.LineNum })
.HasOne(e => e.PurchaseOrder)
.WithMany(e => e.PurchaseOrderLines)
.HasForeignKey(e => { e.Company, e.PONum }); // <--
或
builder.Entity<PurchaseOrder>()
.DefaultConfigure(p => new { p.Company, p.PONum })
.HasMany(e => e.PurchaseOrderLines)
.WithOne(e => e.PurchaseOrder)
.HasForeignKey(e => { e.Company, e.PONum }); // <--
请注意,Has
/ With
对表示一种相同的关系,因此最好只在一个地方进行,以避免配置冲突。
我正在尝试制作几个具有复合键的 table(如下例)。我已经能够使用代码优先方法实现这一点,但我希望这些复合键能够级联到子 tables。这个想法是,每个子实体都将具有与其父实体相同的复合键,再加上一列。
| PurchaseOrder |
| ------------- |
| Company (PK) |
| PONum (PK) |
| PuchaseOrderLine |
| ---------------- |
| Company (PK/FK) |
| PONum (PK/FK) |
| POLine (PK) |
从技术上讲,PurchaseOrder table 会做与公司类似的事情 table 但这对我来说不太重要,我想如果我能够弄清楚 POLine 到 PO 的连接我也会想出那个。
这是我目前的尝试:
// OnModelCreating method in my class that inherits IdentityDbContext<IdentityUser>
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Company>()
.DefaultConfigure(c => new { c.Name });
builder.Entity<PurchaseOrder>()
.DefaultConfigure(p => new { p.Company, p.PONum })
.HasMany(e => e.PurchaseOrderLines)
.WithOne(e => e.PurchaseOrder);
builder.Entity<PurchaseOrderLine>()
.DefaultConfigure(p => new { p.Company, p.PONum, p.LineNum })
.HasOne(e => e.PurchaseOrder)
.WithMany(e => e.PurchaseOrderLines);
}
// My DefaultConfigure extension method
public static EntityTypeBuilder<T> DefaultConfigure<T>(this EntityTypeBuilder<T> builder, Expression<Func<T, object>> keyExpression)
where T : AuditableEntity
{
// Rename table to the types name
builder.ToTable(typeof(T).Name.ToLower());
// Configure the keys they passed in
builder.HasKey(keyExpression);
// Configure the default alternate key
builder.HasAlternateKey(i => new { i.RowId });
// Give the builder back
return builder;
}
当我 运行 此迁移并更新我的数据库时,这是我的 PurchaseOrderLine table 上的内容:
| COLUMN_NAME | CONSTRAINT_NAME |
| -------------------- | -------------------------------------------------------------------------- |
| RowId | AK_PurchaseOrderLine_RowId |
| PurchaseOrderCompany | FK_PurchaseOrderLine_PurchaseOrder_PurchaseOrderCompany_PurchaseOrderPONum |
| PurchaseOrderPONum | FK_PurchaseOrderLine_PurchaseOrder_PurchaseOrderCompany_PurchaseOrderPONum |
| Company | PK_PurchaseOrderLine |
| LineNum | PK_PurchaseOrderLine |
| PONum | PK_PurchaseOrderLine |
EFCore 刚刚使用默认命名方案添加了两个新列,并没有使用我已有的列。有没有办法让 efcore 使用代码优先方法来做这样的事情?
不要这样做。使用自动递增的主键。在父表中查找您需要的值。
复合主键只会让外键变得很麻烦。它们的效率也较低,如果广泛用作外键,通常会使用更多 space。
所以,PurchaseOrder
应该有一个 PurchaseOrderId
。那么PurchaseOrderLine
应该参考PurchaseOrderId
,需要的时候去查一下公司等相关资料
回答你的具体问题
Is there anyway to get efcore to do something like this using the code first approach?
当然有。只是 conventional foreign key names 显然不起作用,因此您必须明确配置 FK 属性(通过 HasForeignKey
fluent API)。
例如或者
builder.Entity<PurchaseOrderLine>()
.DefaultConfigure(p => new { p.Company, p.PONum, p.LineNum })
.HasOne(e => e.PurchaseOrder)
.WithMany(e => e.PurchaseOrderLines)
.HasForeignKey(e => { e.Company, e.PONum }); // <--
或
builder.Entity<PurchaseOrder>()
.DefaultConfigure(p => new { p.Company, p.PONum })
.HasMany(e => e.PurchaseOrderLines)
.WithOne(e => e.PurchaseOrder)
.HasForeignKey(e => { e.Company, e.PONum }); // <--
请注意,Has
/ With
对表示一种相同的关系,因此最好只在一个地方进行,以避免配置冲突。