我的 SQLite 数据库中有一个非标准外键情况,并且我无法控制架构来更改它。有一个父表具有子表的 FK,但 FK 所指向的列不是子表的主键。
我们有:
Parent Child
______ ______
Id Id
...columns ...columns
ExternalId
ChildId --> ExternalId
这在 EF POCO 中建模为:
class Parent
{
public int Id { get; set; }
public int ExternalId { get; set; }
public int ChildId { get; set; }
public Child Child { get; set; }
}
class Child
{
public int Id { get; set; }
public int ExternalId { get; set; }
public int ChildId { get; set; }
}
我的应用程序从 Web API 请求获取父/子数据,这些实体具有来自远程服务器的
ExternalId
。我们将它们序列化到本地 SQLite 数据库,其中 Parent.Id
和 Child.Id
是自动递增的主键,我们想要 Parent.ChildId == Child.ExternalId
。是的,我知道这不是好的范式,但我无法改变它。
Parent.ChildId
由外部服务提供,我不希望在 EF 中设置它,但无论我做什么,当我尝试将父对象添加到数据上下文时,EF 都会将 Parent.ChildId
设置为某些自动递增值,而不是不管它。
即使我删除所有
[ForeignKey]
属性也是如此。
我找不到任何可以正确处理 ChildId FK 的属性组合或流畅的 API 调用。似乎 EF Core 总是假设我希望它设置 ChildId,然后当我尝试将父对象添加到数据库时,就会发生这种情况。
有什么方法可以获得我需要的行为吗?
听起来你的问题不是 FK,而是事实上 FK 指向这个ExternalId,而你分配了一个新的、任意的 PK 列。 (Id) 外键通常指向 PK。
最简单的事情就是接受这些表将由外部系统“拥有”,其中外部 ID 是 PK。如果不将其用于关系,或者让某些关系使用 Id,而其他关系则依赖ExternalId,我不明白通过添加任意自动增量列来充当 PK 有何好处。
或者,您可以显式设置关系以使用ExternalId作为关系的主体:
modelBuilder.Entity<Parent>()
.HasOne(x => x.Child)
.WithOne()
.HasForeignKey<Parent>(x => x.ChildId)
.HasPrincipalKey<Child>(x => x.ExternalId);
您可以使用 Fluent API 来配置关系:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Child>()
.HasIndex(c => c.ExternalId)
.IsUnique(); // ExternalId should be a PK or unique index
modelBuilder.Entity<Parent>()
.HasOne(p => p.Child)
.WithMany()
.HasForeignKey(nameof(Parent.ChildId))
.HasPrincipalKey(nameof(Child.ExternalId));
}
这会创建以下
Parents
表:
CREATE TABLE "Parents" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_Parents" PRIMARY KEY AUTOINCREMENT,
"ExternalId" INTEGER NOT NULL,
"ChildId" INTEGER NOT NULL,
CONSTRAINT "FK_Parents_Children_ChildId" FOREIGN KEY ("ChildId") REFERENCES "Children" ("ExternalId") ON DELETE CASCADE
);
附注
我认为你的术语有点颠倒,如果
Parent
引用Child
那么它实际上是一个子表。