我有以下两个实体:
public class Artist { [Key] public string ArtistId { get; set; } public string Name { get; set; } public virtual ICollection<Genre> Genres { get; set; } } public class Genre { [Key] public int GenreId { get; set; } public string Name { get; set; } public virtual ICollection<Artist> Artist { get; set; } }
在我的程序中,我创建了一些艺术家并希望保存它们:
using (var context = new ArtistContext()) { var artists = _fullArtists.Select(x => x.Artist); foreach (var artist in artists) { context.Artists.AddOrUpdate(artist); } context.SaveChanges(); }
实体框架正确创建了三个表:
艺术家(ArtistId,姓名)
类型(GenreId,姓名)
ArtistGenre(ArtistId,GenreId)
但不幸的是,当我的艺术家样本数据看起来像这样:
var a1 = new Artist { Name = "a1" }; a1.Genres.Add(new Genre { Name="rock"}); var a2 = new Artist { Name = "a2" }; a2.Genres.Add(new Genre { Name="rock"});
它将在表格类型中创建2条记录:
IdName
1个岩
2岩
而不是创建它一次然后重复使用它.
>您知道这是配置问题还是如何告诉EF不插入重复项并重新使用现有的?
提前致谢
编辑:不幸的是,谢尔盖别列佐夫斯基的解决方案没有工作(或者我可能做错了什么:D)
我现在拥有以下内容:
using (var workUnit = WorkUnitFactory.CreateWorkUnit()) { var snapShot = new Snapshot { Date = DateTime.Now.Date }; //ICollection<Genre> genres = _fullArtists.Select(x => x.ToArtist(snapShot)).SelectMany(x => x.Genres).Distinct(new GenreComparer()).ToList(); //workUnit.Repository<IGenreRepository>().InsertEntities(genres); //workUnit.Commit(); var artists = _fullArtists.Select(x => x.ToArtist(snapShot)).ToList(); workUnit.Repository<IArtistRepository>().InsertEntities(artists); workUnit.Commit(); }
ArtistExtensions.cs
public static class ArtistExtensions { public static Artist ToArtist(this FullArtistWrapper value,Snapshot snapShot) { if (value == null) { throw new ArgumentNullException(); } var artist = new Artist { ArtistId = value.Id,Name = value.Name }; var genres = value.Genres.Select(x => x.ToGenre()).ToList(); artist.Genres.AddRange(genres); return artist; } }
GenreExtensions.cs
public static class GenreExtensions { public static Genre ToGenre(this string value) { using (var workUnit = WorkUnitFactory.CreateWorkUnit()) { return workUnit.Repository<IGenreRepository>().GetGenres().FirstOrDefault(x => x.Name == value) ?? new Genre {Name = value}; } } }
不幸的是,EF仍在数据库中插入重复的Genres.
InsertEntities(…)是这样的:
public void InsertEntities<TPersistentEntity>(ICollection<TPersistentEntity> persistentEntitiesToBeInserted) where TPersistentEntity : PersistenceEntity { persistentEntitiesToBeInserted.ForEach(this.Add); } public void Add(PersistenceEntity entity) { var dbSet = this.Context.Set(entity.GetType()); dbSet.Add(entity); }
>我是否误解了谢尔盖的答案,或者EF仍然会插入重复的另一个原因?
再次感谢
解决方法
从EF的角度来看,如果两个实体指向数据库中的同一行,则它们是相同的.即两个实体应该具有相同的非零键.
如果您只想拥有一个名为“rock”的Genre实体,那么您应该将完全相同的类型实体添加到第二个艺术家类型集合中,或者您可以拥有两个实体,但它们应该具有相同的非零ID.我想你有一些Add扩展方法可以创建新类型并将其添加到艺术家的流派:
public static void Add(this ICollection<Genre> genres,string name) { genres.Add(new Genre { Name = name }); }
每次调用此方法时,这将创建独立的类型实例.因此,创建的实体的ID将等于零,EF将它们视为不同的实体.例如.
a1.Genres.Add(new Genre { Name = "rock" }); a1.Genres.Add(new Genre { Name = "rock" });
在保存更改期间,EF将在流派集合中找到两个对象. EF将检查实体ID并生成适当的SQL查询.如果id为零,它将生成INSERT查询.对于非零id,EF将生成UPDATE查询.在这种情况下,您将有两个插入(稍微简化 – 请参阅下面的注释).如何解决?您可以为两位艺术家使用完全相同的流派实体:
var rock = new Genre { Name = "rock" }; var a1 = new Artist { Name = "a1" }; a1.Genres.Add(rock); var a2 = new Artist { Name = "a2" }; a2.Genres.Add(rock);
如果您不想将新的’rock’行插入数据库,那么您可以使用现有的行而不是创建新的:
var rock = db.Genres.FirstOrDefault(g => g.Name == "rock") ?? new Genre { Name = "rock" };