C# 实体框架6插入重复值

C# 实体框架6插入重复值,c#,.net,entity-framework,ef-code-first,entity-framework-6,C#,.net,Entity Framework,Ef Code First,Entity Framework 6,我有以下两个实体: 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;

我有以下两个实体:

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; }
}
实体框架正确创建了三个表:

艺术家(艺术家,姓名)
流派(根里德,名字)
艺人类型(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不要插入重复项,而是重用现有项
提前谢谢


编辑:不幸的是,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();
}
GenreExtensions.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;
        }
    }
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};
            }
        }
    }
  • 我是否误解了Sergey的回答,或者EF仍然插入重复项的另一个原因是什么

再次感谢

在您将艺术家链接到流派的课程中,然后在代码中使用名称字段添加了2个流派

如果你这样做了,你会留在你的艺术家和增加2

a1.Add("rock");
a1.Add("rock");

从EF的角度来看,若两个实体指向数据库中的同一行,那个么它们是相同的。即,两个实体应具有相同的非零键

如果您只希望有一个名为“rock”的
流派
实体,那么您应该将完全相同的流派实体添加到第二个艺术家流派集合中,或者您可以有两个实体,但它们应该具有相同的非零ID。我想您有一些
Add
扩展方法,可以创建新的流派并将其添加到艺术家的流派中:

public static void Add(this ICollection<Genre> genres, string name)
{
    genres.Add(new Genre { Name = name });
}
在保存更改期间,EF将在流派集合中找到两个对象。EF将检查实体ID并生成适当的SQL查询。如果id为零,它将生成INSERT查询。对于非零id,EF将生成更新查询。在这种情况下,您将有两个插入(稍微简化-请参阅下面的注释)。如何解决这个问题?您可以为两位艺术家使用完全相同的流派实体:

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将所有类型添加为新类型

  • 您的基类
    PersistenceEntity
    可能不包括
    Id
    Key
    的属性。您的
    Add
    方法接受
    PersistenceEntity
    main类。这可以将所有对象作为新对象进行处理


  • Genres
    Genres
    对象的集合。您是如何将
    “rock”
    字符串添加到该集合的?抱歉。。当然是a1.Genres.Add(新类型{Name=“rock”});:-)非常感谢您的回答,非常通俗易懂!:-)@xeraphim顺便说一句,我简化了一点查询生成过程-EF实际上跟踪实体的状态。新实体将具有分离状态。将实体添加到某些数据库集后,它会将其状态更改为
    Added
    。实体的状态是EF在生成查询时实际看到的状态。即使实体的ID为零,您也可以手动更改状态并将其设置为
    Modified
    。EF将生成更新查询。但当您尝试保存更改时,它将以并发异常的方式失败:)我尝试实现您的答案,但不幸的是,它没有像我希望的那样工作:-(我已经在我的原始帖子中添加了一个编辑。您介意再看一次,看看我是否误解了您的答案并做错了什么吗?非常感谢:-)@请不要编辑带有后续问题的问题-最好创建新的小问题。但这次我会帮你的。问题是
    workUnit.Commit()。所以,如果db中没有某些流派,您将创建N倍的新流派实体,并为每个艺术家分配新对象。创建新类型时,需要保存更改。EF将为该实体分配ID,您将能够为每个艺术家添加相同的流派实体。谢谢,我回家后会尝试:-)非常感谢Sergey!没错,
    PersistenceEntity
    只是一个空的抽象类。我回家后会尝试添加
    Id
    属性并将其定义为key,谢谢!将
    Id
    属性作为键添加到主实体类提供了一种过滤实体的机制。您的主要目标应该是像
    Add(T实体)
    一样传递参数
    T
    ,而不是
    Add(PersistenceEntity)
     a1.Genres.Add(new Genre { Name = "rock" });
     a1.Genres.Add(new Genre { Name = "rock" });
    
    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);
    
    var rock = db.Genres.FirstOrDefault(g => g.Name == "rock") ?? new Genre { Name = "rock" };