避免在 m:n 关系中使用外键插入重复项

本文关键字:插入 关系 | 更新日期: 2023-09-27 18:32:50

在本文中: Julie Lerman https://msdn.microsoft.com/en-us/magazine/dn166926.aspx 正在解释如何通过设置foreign-key而不是navigation property来避免在1:n关系中插入重复实体。

我有同样的问题,但关系m:n

问题是,我不能只设置外键 id,因为我有一个外键列表。

是否可以像这样使用相同的方法:ICollection<int> TopicIds

如何使用此方法处理m:n关系?

提前致谢

编辑:

以下是实体:

public class Artist : PersistenceEntity
{
    public Artist()
    {
        Genres = new List<Genre>();
    }
    [Key]
    public string ArtistId { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Genre> Genres { get; set; }
}
public class Genre : PersistenceEntity
{
    public Genre() 
    {
        Artists = new List<Artist>();
    }
    [Key]
    public int GenreId { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Artist> Artist { get; set; }
}

避免在 m:n 关系中使用外键插入重复项

如果没有外键字段,则不能使用基于外键的方法。但是,您仍然可以使用基于实体状态的方法。即,如果您正在创建新的艺术家并希望提供一些现有的流派,那么第一次尝试将是

var artist = new Artist }
   Name = "Offspring",
   Genres = new List<Genre> { rockGenre, punkGenre }
};
db.Artists.Add(artist);
db.SaveChanges();

但这会使整个图形进入添加状态(以及两个流派对象(。因此,您应该手动指定流派对象的状态:

db.Genres.Attach(rockGenre);
db.Genres.Attach(punkGenre);
var artist = new Artist { Name = "Offspring" };
db.Artists.Add(artist);
artist.Genres = new List<Genre> { rockGenre, punkGenre };
db.SaveChanges();

var artist = new Artist }
   Name = "Offspring",
   Genres = new List<Genre> { rockGenre, punkGenre }
};
db.Artists.Add(artist);
db.Entry(rockGenre).State = EntityState.Unchanged;
db.Entry(punkGenre).State = EntityState.Unchanged;
db.SaveChanges();

使用导航属性更新多对多关系没有错,事实上,如果您按照 EF 建议的方式配置关系(避免映射联结表,以防您不需要向该表添加其他列(是唯一的方法。

因此,如果您有一个流派 ID 的集合,并且您想将这些流派与艺术家相关联,则可以这样做。

var genresids=new List<int>(){1,3,6,9};
var genres= context.Genres.Where(g=>genresids.Contains(g.GenreId));
var artist=context.Artists.FirstOrDefault(); //Find an artirst
foreach(var genre in genres)
{
   artist.Genres.Add(genre);
}
context.SaveChanges();

现在,如果您决定将联结表映射为实体,则需要创建两个一对多关系来替换多对多配置。您的交汇点实体将如下所示:

public class GenreArtist
{
    [Key,Column(Order=1), ForeignKey("Artist")]
    public string ArtistId { get; set; }
    [Key,Column(Order=2), ForeignKey("Genre")]
    public int GenreId { get; set; }
    public virtual Artist Artist { get; set; }
    public virtual Genre Genre { get; set; }
}

并且必须将集合导航属性的类型更改为GenreArtist

public virtual ICollection<GenreArtist> GenreArtists{ get; set; }

使用该模型,您可以通过以下方式将多个流派与艺术家相关联:

var genresids=new List<int>(){1,3,6,9};
var artist=context.Artists.FirstOrDefault(); //Find the artirst you want to update
foreach(var genreId in genresids)
{
   artist.GenreArtists.Add(new GenreArtist{ArtistId=artist.ArtistId, GenereId=genreId});
}
context.SaveChanges();

但我真的相信,如果您的连接表不会有更多列,则没有必要这样做。无论如何,在此链接中,您可以找到支持映射该表的想法的充分理由。