Entity Framework 6 插入重复值
Entity Framework 6 inserting duplicate values
我有以下两个实体:
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();
}
Entity Framework 正确创建了三个 tables:
艺术家(ArtistId,姓名)
流派(GenreId,名称)
艺术家流派 (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"});
它将在 table 中创建 2 条记录 Genre
:
身份证姓名
1 摇滚
2 摇滚
而不是创建一次然后重用它。
- 您知道这是否是配置问题或如何告诉 EF 不要插入重复项并重用现有的吗?
提前致谢
编辑: 不幸的是,Sergey Berezovskiy 的解决方案没有用(或者可能是我做错了什么 :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);
}
- 我是不是误解了 Sergey 的回答,或者 EF 仍然插入重复项的另一个原因是什么?
再次感谢
在 类 中,您将艺术家链接到流派,然后在代码中使用名称字段添加了 2 流派。
如果你这样做了,你会留在你的艺术家中并添加 2
a1.Add("rock");
a1.Add("rock");
从 EF 的角度来看,如果两个实体指向数据库中的同一行,则它们是相同的。 IE。两个实体应该有相同的非零键。
如果您只想拥有一个名为 "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" };
我认为有两个可能的原因不起作用:
您只在代码块末尾提交一次。因此,EF 将所有流派添加为一个新流派。
您的基础 class PersistenceEntity
可能不包括 Id
属性 即 Key
。并且您的 Add
方法接受 PersistenceEntity
main class。这可以影响所有对象作为一个新对象。
我有以下两个实体:
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();
}
Entity Framework 正确创建了三个 tables:
艺术家(ArtistId,姓名)
流派(GenreId,名称)
艺术家流派 (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"});
它将在 table 中创建 2 条记录 Genre
:
身份证姓名
1 摇滚
2 摇滚
而不是创建一次然后重用它。
- 您知道这是否是配置问题或如何告诉 EF 不要插入重复项并重用现有的吗?
提前致谢
编辑: 不幸的是,Sergey Berezovskiy 的解决方案没有用(或者可能是我做错了什么 :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);
}
- 我是不是误解了 Sergey 的回答,或者 EF 仍然插入重复项的另一个原因是什么?
再次感谢
在 类 中,您将艺术家链接到流派,然后在代码中使用名称字段添加了 2 流派。
如果你这样做了,你会留在你的艺术家中并添加 2
a1.Add("rock");
a1.Add("rock");
从 EF 的角度来看,如果两个实体指向数据库中的同一行,则它们是相同的。 IE。两个实体应该有相同的非零键。
如果您只想拥有一个名为 "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" };
我认为有两个可能的原因不起作用:
您只在代码块末尾提交一次。因此,EF 将所有流派添加为一个新流派。
您的基础 class
PersistenceEntity
可能不包括Id
属性 即Key
。并且您的Add
方法接受PersistenceEntity
main class。这可以影响所有对象作为一个新对象。