实体框架允许在多对多关系中复制

本文关键字:关系 复制 框架 实体 | 更新日期: 2023-09-27 18:13:09

我有2个对象-父母和孩子的关系多对多,问题是一个父母可以有2个相同的孩子,但EF保存这种关系只有1次。

我发现只有两个可行的解决方案:

  1. 添加count列到表中并手动填充
  2. 不使用多对多,而是分成一对多和多对一但我不喜欢这样的解决方案,因为我希望能有更简单的解决方案。

你能帮我吗?

编辑:连接表示例:

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");
});

实体框架允许在多对多关系中复制

关系数据库是关于集合的。集合是标识实体的独特集合。然而,你的连接表,你想象它的方式,是一个。这违背了关系理论的基本原理。像往常一样,一项违规行为会引起其他违规行为。下一个问题是没有唯一的主键,即标识。如果需要的话,不能通过外键引用这些连接记录是另一个问题。

所以不要这样做。

我认为这里的错误是你想通过你创建的行数来表达"AnB实例"这一事实。但是这个数字是关联的一个属性。

让我们来看一个例子:Articles and Words

可以用纯多对多关联来表示ArticleWord之间的关系。这个关联表示:这个 Article包含这些 Word。注意指示代词(this, these)。它们暗示着身份。在数据库中,每篇文章和每个单词都有一个实例。

如果你想存储一个单词出现的次数,你必须把这个数字作为一个属性添加到关联中。事实永远不会由行数来建模。单行描述了"这个Articlen次出现那个Word"这一事实。在实体框架中,这意味着关联成为类模型中的类,例如ArticleWordArticle 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();