Entity Framework 6 by inserting duplicate values

I have the following two objects:

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

In my program, I create some artists and want to save them:

 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 correctly created three tables:

Artist (ArtistId, Name)
Genre (GenreId, Name)
ArtistGenre (ArtistId, GenreId)

But unfortunately, when my sample data samples look like this:

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

It will create 2 entries in the Genre table:

Id name
1 rock
2 rock

instead of creating it once and then reusing it.

  • Do you know that this is a configuration problem or how to tell EF not to insert duplicates and reuse existing ones instead?

Thank you in advance


Edit: Unfortunately, Sergey Berezovsky’s solution doesn't work (maybe I did something wrong: D)

Now I have the following:

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

Unfortunately, EF still inserts a duplicate of Genres into the database.

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); } 
  • Did I misunderstand Sergey’s answer or what could be another reason that EF is still inserting duplicates?

Thanks again

+5
source share
3 answers

From an EF point of view, two objects are the same if they point to the same row in the database. That is, two objects must have the same non-zero keys.

If you want to have only one Genre object named "rock", then you must add the exact same genre entity to the second collection of artist genres, or you can have two entities, but they must have the same non-zero identifiers. I assume that you have an Add extension method that creates a new genre and adds it to artist genres:

 public static void Add(this ICollection<Genre> genres, string name) { genres.Add(new Genre { Name = name }); } 

This will create independent instances of genres every time you call this method. Thus, the identifiers of the created objects will be equal to zero, EF will consider them as different objects. For instance.

  a1.Genres.Add(new Genre { Name = "rock" }); a1.Genres.Add(new Genre { Name = "rock" }); 

While saving changes, EF will find two objects in the genre collection. EF checks object identifiers and generates the corresponding SQL queries. If id is zero, it will generate an INSERT request. For nonzero id, EF will generate an UPDATE query. In this case, you will have two inserts (slightly simplified - see the comment below). How to fix it? You can use the exact same genre entity for both artists:

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

If you do not want to insert a new string "rock" into the database, you can use the existing one instead of the new one:

 var rock = db.Genres.FirstOrDefault(g => g.Name == "rock") ?? new Genre { Name = "rock" }; 
+2
source

In the classes that you linked to Artist to Genre, and then in the code that you added 2 Genre, using the Name field.

If you did, you would remain in your artist and add 2

 a1.Add("rock"); a1.Add("rock"); 
+1
source

I think there are two possible reasons that do not work:

  • You commit only once and at the end of a code block. Thus, EF adds all genres as a new one.

  • Your base class PersistenceEntity may not include the Id property, which is Key . And your Add method takes the PersistenceEntity core class. This may affect the whole object as new.

+1
source

Source: https://habr.com/ru/post/1241980/


All Articles