EF Core - 具有相同依赖实体类型的附加导航 属性 的一对多关系

EF Core - One to many relationship with additional navigation property of same dependent entity type

我在 EF Core 中配置关系时遇到问题。我遇到了以下异常 -

Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints

我已经为这个 post 削减了实体,但是这两个实体都有自己的 table。

public class ApplicationSetupTest
{
    public Guid Id { get; set; }
    public Guid SchemeId { get; set; }
    public string Description { get; set; }

    public Guid LatestVersionId { get; set; }
    public ApplicationSetupVersionTest LatestVersion { get; set; }

    public ICollection<ApplicationSetupVersionTest> VersionHistory { get; set; }
}

public class ApplicationSetupVersionTest
{
    public Guid Id { get; set; }
    public Guid SetupId { get; set; }
    public string Data { get; set; }
    public string AuditComment { get; set; }
    public Guid PreviousVersionId { get; set; }
}

ApplicationSetupTest class 有效地定义了带有 LatestVersionId 的静态数据,它是导航键 属性 LatestVersion.

ApplicationSetupVersionTest class 是 versioned/audited 数据。这些中的每一个都有一个 SetupId 到 link 它回到 ApplicationSetupTest 是指。

我添加 VersionHistory 属性 纯粹是为了这个 post 以证明每个 ApplicationSetupTest 上可以有多个 ApplicationSetupVersionTest。我没有在 ApplicationSetupVersionTest 上添加 ApplicationSetupTest,因为这不是我期望需要的东西。

我的 ApplicationSetupTest 配置如下:

public class ApplicationSetupEntityConfiguration : IEntityTypeConfiguration<ApplicationSetupTest>
{
    public void Configure(EntityTypeBuilder<ApplicationSetupTest> builder)
    {
        builder.Property(t => t.SchemeId).IsRequired();
        builder.Property(t => t.Description).IsRequired();
        builder.Property(t => t.LatestVersionId).IsRequired();

        builder.HasMany(t => t.VersionHistory)
            .WithOne()
            .HasForeignKey(t => t.SetupId)
            .IsRequired();

        builder.HasOne(t => t.LatestVersion)
            .WithOne()
            .HasForeignKey<ApplicationSetupTest>(t => t.LatestVersionId)
            .OnDelete(DeleteBehavior.NoAction)
            .IsRequired();

        builder.HasOne<Scheme>()
            .WithMany()
            .HasForeignKey(t => t.SchemeId)
            .IsRequired();
    }
}

VersionHistory 上的 HasMany -> WithOne 定义了当我删除设置时,我应该删除所有版本实体。

因此我假设第二个配置是要更改的区域。 OnDelete(NoAction) 是在 Google 搜索之后添加的,我还尝试删除 IsRequired() 以及使 LatestVersionId 可为空。

我希望配置第二个关系,以便 LatestVersion 属性 可以包含在查询中。

关于如何配置这种关系有什么想法吗?还是我在做一些你不推荐的事情?

(为简单起见,我将模型称为 SetupVersion)。

有了你的一对多配置 -

builder.HasMany(t => t.VersionHistory)
    .WithOne()
    .HasForeignKey(t => t.SetupId)
    .IsRequired();

你声明了Setup为主端,Version为从属端,是正确的。

但是你在Setup中有一个LatestVersionId外键,引用Version,一对一关系的配置-

builder.HasOne(t => t.LatestVersion)
    .WithOne()
    .HasForeignKey<ApplicationSetupTest>(t => t.LatestVersionId)
    .OnDelete(DeleteBehavior.NoAction)
    .IsRequired();

正在尝试将 Setup 配置为从属端,将 Version 配置为主体端。我相信你能看出其中的矛盾。

使用以下简化模型 -

public class Setup
{
    public Guid Id { get; set; }
    public string Description { get; set; }

    public Version LatestVersion { get; set; }
    public ICollection<Version> VersionHistory { get; set; }
}

public class Version
{
    public Guid Id { get; set; }        
    public string Data { get; set; }

    // not nullable - every Version must belong to a Setup
    public Guid SetupIdHistory { get; set; }

    // nullable - not every Version is a latest version
    public Guid? SetupIdLatest { get; set; }
}

您可以正确配置它们以将您的关系表示为 -

public void Configure(EntityTypeBuilder<Setup> builder)
{
    builder.HasMany(p => p.VersionHistory)
        .WithOne()
        .HasForeignKey(p => p.SetupIdHistory)
        .OnDelete(DeleteBehavior.Cascade)  // not required, cascading is default
        .IsRequired();

    builder.HasOne(p => p.LatestVersion)
        .WithOne()
        .HasForeignKey<Version>(p => p.SetupIdLatest)
        .OnDelete(DeleteBehavior.NoAction)
        .IsRequired(false);
}

如果您选择不为一对多关系设置外键,EF 将为您创建一个可为空的外键,并在模型级别使用影子管理关系 属性。但是对于一对一的关系,你必须定义一个外键。

public class Version
{
    public Guid Id { get; set; }        
    public string Data { get; set; }

    // nullable - not every Version is a latest version
    public Guid? SetupId { get; set; }
}
public void Configure(EntityTypeBuilder<Setup> builder)
{
    builder.HasMany(p => p.VersionHistory)
        .WithOne()
        .OnDelete(DeleteBehavior.Cascade)
        .IsRequired();    // this will have no effect, the FK will be nullable

    builder.HasOne(p => p.LatestVersion)
        .WithOne()
        .HasForeignKey<Model.Version>(p => p.SetupId)
        .OnDelete(DeleteBehavior.NoAction)
        .IsRequired(false);
}