使用带有桥接表的 EF Core 7 约定实现查询和级联删除时遇到问题

Having trouble achieving queries and cascading deletes using EF Core 7 conventions with a bridge table

提问人:aardvark 提问时间:9/19/2023 更新时间:9/19/2023 访问量:27

问:

在我的 SQL 数据库中,我有一个表。我还有一张桌子。单元可以具有 Containers,并且该关系是与表建立的,该表包含 ContainerId - Containers 表的 ContainerId 列的外键;以及 UnitId,它是返回到 Units 表上的 UnitId 的外键。UnitId 不直接位于 Containers 表上的原因是,容器也可以属于其他类型的实体,因此决定为每个类型设置一个桥接表,而不是每次想要将新类型与容器关联时都更改表定义。UnitsContainersUnitContainers

在 OnModelCreating() 中,目前我有以下内容:

modelBuilder.Entity<UnitContainer>().HasKey(uc => new { uc.UnitId, uc.ContainerId });

modelBuilder.Entity<Unit>()
            .HasMany(u => u.UnitContainers)
            .WithOne()
            .HasForeignKey(uc => uc.UnitId)
            .OnDelete(DeleteBehavior.Cascade);

        modelBuilder.Entity<Container>()
            .HasMany(c => c.UnitContainers)
            .WithOne()
            .HasForeignKey(uc => uc.ContainerId)
            .OnDelete(DeleteBehavior.Cascade);

        modelBuilder.Entity<UnitContainer>()
            .HasKey(uc => new { uc.UnitId, uc.ContainerId });

        modelBuilder.Entity<UnitContainer>()
            .HasOne(uc => uc.Unit)
            .WithMany(u => u.UnitContainers)
            .HasForeignKey(uc => uc.UnitId);

        modelBuilder.Entity<UnitContainer>()
            .HasOne(uc => uc.Container)
            .WithMany(c => c.UnitContainers)
            .HasForeignKey(uc => uc.ContainerId);

我已经尝试了配置的其他几种排列,但它们几乎都会导致相同的结果,也就是说,当我去执行这个 linq 查询时:

var query = ctx.Set<Unit>()
            .Include(unit => unit.Containers);

我收到一个 SqlException:

Microsoft.Data.SqlClient.SqlException (0x80131904):列无效 名称“UnitId”。列名“UnitId”无效。

这是因为生成的 SQL 尝试直接将容器 LEFT JOIN 到 Units,而不是使用 UnitContainers 桥接表。

将这部分视为我的问题之一。第二部分是我不仅需要它才能工作,我还需要级联删除才能工作,以便当我删除一个单元时,在 UnitContainers 中引用该单元的任何条目也会被删除。

谁能告诉我我的配置有什么问题?

下面是 Unit 实体:

public class Unit
{
    [Key]
    UnitId { get; set; }
    public ICollection<UnitContainer> UnitContainers { get; set; }
    public ICollection<Container> Containers { get; set; }
}

下面是容器实体:

public class Container
{
    [Key]
    public int ContainerId { get; set; }
    public int UnitId { get; set; }

    public Unit Unit { get; set; }
    public ICollection<UnitContainer> UnitContainers { get; set; }
}

提前致谢。

C# SQL .NET 实体框架核心

评论

0赞 user16606026 9/19/2023
具有代码优先的多对多映射
0赞 aardvark 9/24/2023
我无法让它与级联删除一起使用。最后只是手动完成它们。

答:

0赞 Wes H 9/19/2023 #1

我相信您已经多次配置了属性关系。 在这个...UnitConfigurations

modelBuilder.Entity<Unit>()
            .HasMany(u => u.UnitContainers)
            .WithOne()
            .HasForeignKey(uc => uc.UnitId)
            .OnDelete(DeleteBehavior.Cascade);

您声明了单方面的关系,有一个集合,但不知道从...UnitUnitContainersUnitConfigurationUnit.WithOne()

然后你又宣布了财产关系......UnitContainers

        modelBuilder.Entity<UnitContainer>()
            .HasOne(uc => uc.Unit)
            .WithMany(u => u.UnitContainers)
            .HasForeignKey(uc => uc.UnitId);

这一次,存在互惠关系 两者都在配置相同的关系,并且都配置了属性。但是,同一实体之间的两个不同关系不能共享同一个密钥。.HasOne(uc=>uc.Unit)UnitContainers

要么将两个子句都留空,...With

modelBuilder.Entity<Unit>()
            .HasMany(u => u.UnitContainers)
            .WithOne()
            .HasForeignKey(uc => uc.UnitId)
            .OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<UnitContainer>()
            .HasOne(uc => uc.Unit)
            .WithMany()
            .HasForeignKey(uc => uc.UnitId);

或者摆脱这种关系并使用已经定义的。UnitUnitConfiguration

作为奖励... 如果要跳过中间导航,例如直接导航到 from,或者相反,可以尝试以下配置:UnitConfigurationList<Configuration> ConfigurationsUnit

modelbuilder.Entity<Unit>
   .HasMany(x=>x.Configurations)
   .WithMany()
   .UsingEntity<UnitConfiguration>();