EF Core - 级联删除以相反的方式发生

EF Core - Cascade deletes happening the other way around

我有一个简单的 EF Core 模型,它包含两个实体:位置和地址,其中位置必须始终具有地址。

实体及其配置如下:

public class Location {
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
    public int AddressId { get; set; }
    [ForeignKey("AddressId")]
    public virtual Address Address { get; set; }
}
public class Address {
    public virtual int AddressId { get; set; }
    public string LineOne { get; set; }
    public string LineTwo { get; set; }
    public string City { get; set; }
    public string Country { get; set; }
    public string ZipPostCode { get; set; }
    public string State { get; set; }
    public string LandlinePhone { get; set; }
    public string MobilePhone { get; set; }
}

// Context configuration
...
protected override void OnModelCreating(ModelBuilder modelBuilder) {
...
modelBuilder.Entity<Location>().HasOne(l => l.Address).WithOne().OnDelete(DeleteBehavior.Cascade);
...
}
...

注意:地址实体没有返回位置的引用。

使用上述配置,删除位置应该删除其地址,但删除地址会导致引用完整性错误。使用 EF InMemory 数据库通过单元测试尝试上述操作会产生意想不到的结果:

    [Test]
    public void DeleteAddressShouldNotDeleteLocation() {
        var address = new Address();
        var location = new Location() {Address = address};
        _context.Locations.Add(location);
        _context.SaveChanges();

        // Delete the address added above
        _context.Addresses.Remove(_context.Addresses.Single());
        _context.SaveChanges();

        Assert.AreEqual(1, _context.Locations.Count()); --> ALWAYS FAILS AS THE LOCATION IS CASCADE DELETED
    }

这也发生在我们的生产服务器 (Azure SQL) 上,因此它不应该是 EF Core 的问题。

EF 将地址删除级联到 Location 是否有原因?

With the above configuration, deleting a location should delete its address but deleting the address should result in an reference integrity error

否定。每个关系(多对多除外)都有 principaldependent(参见 Relationships - Definitions of terms)。级联删除从主体(引用)到依赖(引用)。如果级联删除被禁用,那么如果有对它的引用则不能删除主体。

那么,在您的案例中,谁是受抚养人?依赖者始终是对另一个人有 FK 参考的人,即 Location,因此您正在经历的行为。

如果你想换个方式,从 Location 中删除 AddressId FK,并将 LocationId FK 添加到 Address(或 configure/use通过使 Address PK 也成为 FK,所谓的“共享主键关联”。

但请注意,没有关系设计可以满足您的所有要求。最接近的(将满足 3 个中的 2 个)是使用 DeleteBehavior.Restrict 保持当前设计。这样

  1. 位置必须始终有一个地址 - 满足。
  2. 删除地址会导致引用完整性错误 - 满意
  3. 删除位置应该删除其地址 - 不满意,也不可能,因为该位置不拥有该地址。

尝试使用 .OnDelete(DeleteBehavior.Restrict) - 违反直觉,但适用于我的项目。
可以把它想象成“如果有地址就不要删除地址”。

此关系的附加 .IsRequired() 将强制使用一个地址 - 正如您在问题中所述(位置始终有一个地址)。