实体框架允许在多对多关系中复制
本文关键字:关系 复制 框架 实体 | 更新日期: 2023-09-27 18:13:09
我有2个对象-父母和孩子的关系多对多,问题是一个父母可以有2个相同的孩子,但EF保存这种关系只有1次。
我发现只有两个可行的解决方案:
- 添加
count
列到表中并手动填充 - 不使用多对多,而是分成一对多和多对一但我不喜欢这样的解决方案,因为我希望能有更简单的解决方案。
你能帮我吗?
编辑:连接表示例:
1 - 1
1 - 1
1 - 21 - 3
2 - 3
代码:模型
public class Item
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int id { get; set; }
public virtual ICollection<Item> childs { get; set; }
public virtual ICollection<Item> parents { get; set; }
}
DB上下文
modelBuilder.Entity<Item>().
HasMany(i => i.childs).
WithMany(i2 => i2.parents).
Map(
m =>
{
m.MapLeftKey("parentId");
m.MapRightKey("childId");
m.ToTable("itemRelationship");
});
关系数据库是关于集合的。集合是标识实体的独特集合。然而,你的连接表,你想象它的方式,是一个包。这违背了关系理论的基本原理。像往常一样,一项违规行为会引起其他违规行为。下一个问题是没有唯一的主键,即标识。如果需要的话,不能通过外键引用这些连接记录是另一个问题。
所以不要这样做。
我认为这里的错误是你想通过你创建的行数来表达"A
有n
的B
实例"这一事实。但是这个数字是关联的一个属性。
让我们来看一个例子:Articles and Words
可以用纯多对多关联来表示Article
和Word
之间的关系。这个关联表示:这个 Article
包含这些 Word
。注意指示代词(this, these)。它们暗示着身份。在数据库中,每篇文章和每个单词都有一个实例。
如果你想存储一个单词出现的次数,你必须把这个数字作为一个属性添加到关联中。事实永远不会由行数来建模。单行描述了"这个Article
有n
次出现那个Word
"这一事实。在实体框架中,这意味着关联成为类模型中的类,例如ArticleWord
。Article
1-n ArticleWord
n-1 Word
。
一个解决方案是首先定义关系表。该表的主键将是它包含的两个外键的组合,它将允许组合键看起来像:
1 - 1,
2,
2 - 2
但不:
1 - 1
1 - 1
1 - 22 - 1
我同意Gert关于关系数据的核心概念是集合的观点。为此,我建议你找到另一件事来让它们与众不同。比如添加时间戳或按日期创建。如果你真的需要副本的话。但这又回到了关系数据被归一化为集合的问题。这是一个关于数据库规范化的链接
[Key, Column(Order =2), DatabaseGenerated(DatabaseGeneratedOption.None)]
public DateTime DateCreated { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
时间戳主要用于并发,但您也可以将它们添加到键中。
我也很好奇你在多对多表中需要副本的场景是什么。只有两列完全相同的两条记录,意味着关系被表达了两次,它不会创建一个额外的关系。
这就像说我的妹妹就是我的妹妹,他说她是我的妹妹并没有改变她是我妹妹的事实,它没有给我两个她,她仍然在集合中,因为数据库规范化,她不可能在集合中出现两次。我可以数她两次,但这并不能改变她(在另一张表中)只有一个的事实。如果需要两个她,那么应该有另一个具有不同Id的记录。
如果您确实需要在n:n关系上有重复记录:
public class Playlist : BaseEntity
{
public Collection<Track>? Tracks { get; set; }
}
public class Track: BaseEntity
{
public IEnumerable<Playlist>? Playlists { get; set; }
}
public class PlaylistTrack
{
public int Id { get; set; }
public int PlaylistId { get; set; }
public int TrackId { get; set; }
}
//....
public DbSet<PlaylistTrack> PlaylistTrack { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Playlist>()
.HasMany(a => a.Tracks)
.WithMany(a => a.Playlists)
.UsingEntity<PlaylistTrack>(a =>
a.Property(e => e.Id).ValueGeneratedOnAdd()
);
}
插入记录
var pt = new PlaylistTrack()
{
PlaylistId = playlist.Id,
TrackId = track.Id
};
db.PlaylistTrack.Add(pt);
await db.SaveChangesAsync();