EF Core - 当我指定自己的外键时,会自动添加不需要的外键

EF Core - Unwanted foreign keys being added automatically when I specify my own ones

我的 GroceryItemGroceryStores 多对多连接 table 中似乎有重复的外键:VeganItemIdVeganItemsIdEstablishmentIdEstablishmentsId.

我实际上只是在使用 VeganItemIdEstablishmentId,它们是唯一被添加到的。它会自动向我的 table 添加 VeganItemsIdEstablishmentsId 列。我怎么告诉它不要这样做?:

我的数据库的这张图片显示了有效的外键:

我的数据库上下文:

modelBuilder.Entity<GroceryItem>(gi =>
{
    gi.HasIndex(e => new { e.Brand, e.Name }).IsUnique();
    gi.HasKey(e => e.Id);
    gi.Property(e => e.Tags)
    .HasConversion(
        v => JsonSerializer.Serialize(v, null),
        v => JsonSerializer.Deserialize<List<GroceryItemTag>>(v, null),
        new ValueComparer<IList<GroceryItemTag>>(
            (c1, c2) => c1.SequenceEqual(c2),
            c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
            c => (IList<GroceryItemTag>)c.ToList()));

});

modelBuilder.Entity<GroceryStore>(gs =>
{
    gs.HasIndex(gs => gs.PlaceId).IsUnique();

    gs.HasMany(gs => gs.VeganItems)
    .WithMany(vi => vi.Establishments)
    .UsingEntity<GroceryItemGroceryStore>
        (gigs => gigs.HasOne<GroceryItem>().WithMany(),
        gigs => gigs.HasOne<GroceryStore>().WithMany());
});

modelBuilder.Entity<GroceryItemGroceryStore>(gigs =>
{
    gigs.HasIndex(e => new { e.VeganItemId, e.EstablishmentId }).IsUnique();
    gigs.HasKey(e => new { e.VeganItemId, e.EstablishmentId });
});

public DbSet<GroceryItem> GroceryItems { get; set; }
public DbSet<GroceryItemGroceryStore> GroceryItemGroceryStores { get; set; }
public DbSet<GroceryStore> GroceryStores { get; set; }

我的tables:

public class GroceryStore
{   
    [Key]
    public Int64 Id { get; set; }
    [Required]
    public string Name { get; set; }
    [Required]
    public string PlaceId { get; set; }
    [Required]
    public string Street { get; set; }
    public string Suburb { get; set; }
    public string City { get; set; }
    public string StreetNumber { get; set; }

    public virtual ICollection<GroceryItem> VeganItems { get; set; }

}

public class GroceryItem
{
    [Key]
    public Int64 Id { get; set; }
    [Required]
    public string Name { get; set; }
    [Required]
    public string Description { get; set; }
    public string Image { get; set; }
    [Required]
    public int IsNotVeganCount { get; set; }
    [Required]
    public int IsVeganCount { get; set; }
    [Required]
    public int RatingsCount { get; set; }
    [Required]
    public int Rating { get; set; }
    [Required]
    public List<GroceryItemTag> Tags { get; set; }
    [Required]
    public int CurrentRevisionId { get; set; }
    public virtual ICollection<GroceryStore> Establishments { get; set; }
}

public class GroceryItemGroceryStore
{
    [Key]
     public Int64 Id { get; set; }
    [Key, Column(Order = 0), Required]
    public int VeganItemId { get; set; }
    [Key, Column(Order = 1), Required]
    public int EstablishmentId { get; set; }

    public virtual GroceryItem VeganItem { get; set; }
    public virtual GroceryStore Establishment { get; set; }

    [Required]
    public int NotInEstablishmentCount { get; set; }
    [Required]
    public int InEstablishmentCount { get; set; }
    [Required]
    public double Price { get; set; }

}

甚至像这样添加 .HasForeignKey(o => o.VeganItemId):

modelBuilder.Entity<GroceryStore>(gs =>
{
    gs.HasIndex(gs => gs.PlaceId).IsUnique();

    gs.HasMany(gs => gs.VeganItems)
    .WithMany(vi => vi.Establishments)
    .UsingEntity<GroceryItemGroceryStore>
        (gigs => gigs.HasOne<GroceryItem>().WithMany().HasForeignKey(o => o.EstablishmentId),
        gigs => gigs.HasOne<GroceryStore>().WithMany().HasForeignKey(o => o.VeganItemId));
});

使 table 有 VeganItemId1EstablishmentId1:

编辑:我已经尝试从所有迁移文件中删除所有出现的 VeganItemsIdEstablishmentsId,但是当插入到数据库时它仍然认为它需要插入 EstablishmentsId

此类 FK 的存在清楚地表明关系配置错误,通常发生在您从流畅配置中遗漏一些关系导航属性时,在这种情况下,EF 将它们映射到具有传统 FK 的单独 FK 关系 property/column 名字。

在这种特殊情况下,错误配置就在这里

gigs => gigs.HasOne<GroceryItem>().WithMany() // (1)

gigs.HasOne<GroceryStore>().WithMany() // (2)

因为您遗漏了 GroceryItemGroceryStore

的导航属性
public virtual GroceryItem VeganItem { get; set; } // (1)
public virtual GroceryStore Establishment { get; set; } // (2)

可能它们最初不存在,您后来添加了它们。但是你应该始终保持流畅的配置与模型同步,在这种情况下当然应该像

gigs => gigs.HasOne(e => e.VeganItem).WithMany()

gigs.HasOne(e => e.Establishment).WithMany()

您的问题是您已将 VeganItemIdEstablishmentId 定义为 复合 主键 的一部分GroceryItemGroceryStore 的,与 Id

Artificitial 键结合使用

It is not necessary and even counter productive to define a composite key that includes a column that is already unique for all rows in the table, if there is a single column that is unique for all records, and you were going to include it in the primary key, then you should just use that column as the PK.

您的流畅配置与您的属性配置冲突,我建议至少部分解决方案如下:

public class GroceryItemGroceryStore
{
    [Key]
    public Int64 Id { get; set; }
    [ForeignKey(nameof(VeganItem)), Column(Order = 0), Required]
    public int VeganItemId { get; set; }
    [ForeignKey(nameof(Establishment)), Column(Order = 1), Required]
    public int EstablishmentId { get; set; }

    public virtual GroceryItem VeganItem { get; set; }
    public virtual GroceryStore Establishment { get; set; }

    [Required]
    public int NotInEstablishmentCount { get; set; }
    [Required]
    public int InEstablishmentCount { get; set; }
    [Required]
    public double Price { get; set; }

}
  1. GroceryItemGroceryStore中将VeganItemIdEstablishmentId的类型改为Int64,使它们与GroceryItemGroceryItem中对应主键的类型匹配GroceryStore -
[Column(Order = 0), Required]
public Int64 VeganItemId { get; set; }       // Key attribute is not needed here
[Column(Order = 1), Required]
public Int64 EstablishmentId { get; set; }   // Key attribute is not needed here
  1. 修改 GroceryStore 的配置以包含导航属性并显式配置外键 -
builder.Entity<GroceryStore>(gs =>
{
    gs.HasIndex(gs => gs.PlaceId).IsUnique();

    gs.HasMany(gs => gs.VeganItems)
    .WithMany(vi => vi.Establishments)
    .UsingEntity<GroceryItemGroceryStore>
        (gigs => gigs.HasOne(p => p.VeganItem)
                    .WithMany().HasForeignKey(p => p.VeganItemId),
        gigs => gigs.HasOne(p => p.Establishment)
                    .WithMany().HasForeignKey(p => p.EstablishmentId));
});

这应该可以解决重复键问题。

此外,您应该从 GroceryItemGroceryStore -

中删除以下 属性
[Key]
public Int64 Id { get; set; }

因为您正在通过 fluent 配置复合主键 API。