EF Core - 当我指定自己的外键时,会自动添加不需要的外键
EF Core - Unwanted foreign keys being added automatically when I specify my own ones
我的 GroceryItemGroceryStores
多对多连接 table 中似乎有重复的外键:VeganItemId
、VeganItemsId
、EstablishmentId
、EstablishmentsId
.
我实际上只是在使用 VeganItemId
和 EstablishmentId
,它们是唯一被添加到的。它会自动向我的 table 添加 VeganItemsId
和 EstablishmentsId
列。我怎么告诉它不要这样做?:
我的数据库的这张图片显示了有效的外键:
我的数据库上下文:
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 有 VeganItemId1
和 EstablishmentId1
:
编辑:我已经尝试从所有迁移文件中删除所有出现的 VeganItemsId
和 EstablishmentsId
,但是当插入到数据库时它仍然认为它需要插入 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()
您的问题是您已将 VeganItemId
和 EstablishmentId
定义为 复合 主键 的一部分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; }
}
- 在
GroceryItemGroceryStore
中将VeganItemId
和EstablishmentId
的类型改为Int64
,使它们与GroceryItem
和GroceryItem
中对应主键的类型匹配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
- 修改
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。
我的 GroceryItemGroceryStores
多对多连接 table 中似乎有重复的外键:VeganItemId
、VeganItemsId
、EstablishmentId
、EstablishmentsId
.
我实际上只是在使用 VeganItemId
和 EstablishmentId
,它们是唯一被添加到的。它会自动向我的 table 添加 VeganItemsId
和 EstablishmentsId
列。我怎么告诉它不要这样做?:
我的数据库的这张图片显示了有效的外键:
我的数据库上下文:
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 有 VeganItemId1
和 EstablishmentId1
:
编辑:我已经尝试从所有迁移文件中删除所有出现的 VeganItemsId
和 EstablishmentsId
,但是当插入到数据库时它仍然认为它需要插入 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()
您的问题是您已将 VeganItemId
和 EstablishmentId
定义为 复合 主键 的一部分GroceryItemGroceryStore
的,与 Id
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; }
}
- 在
GroceryItemGroceryStore
中将VeganItemId
和EstablishmentId
的类型改为Int64
,使它们与GroceryItem
和GroceryItem
中对应主键的类型匹配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
- 修改
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。