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; 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 правильно создал три таблицы:

Художник (ArtistId, Имя)
Жанр (GenreId, Name)
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 записи в таблице Genre:

Idимя
1 рок
2 рок

вместо того, чтобы создавать его один раз, а затем повторно использовать его.

  • знаете ли вы, если это проблема конфигурации или как сказать EF, чтобы не вставлять дубликаты и повторно использовать существующие вместо этого?

спасибо заранее


Edit: к сожалению, решение Сергея Березовского не сработало (или, возможно, я сделал что-то не так :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 все еще вставляет дубликаты?

еще раз спасибо

3 ответов


С точки зрения EF два объекта одинаковы, если они указывают на одну и ту же строку в базе данных. Т. е. два объекта должны иметь одинаковые ненулевые ключи.

если вы хотите иметь только один тег Genre entity с именем "rock", то вы должны добавить точно такой же жанр entity во вторую коллекцию жанров исполнителя или вы можете иметь два объекта, но они должны иметь одинаковые ненулевые идентификаторы. Полагаю, у вас есть немного!--5--> метод расширения, который создает новый жанр и добавляет его в художника жанры:

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

это создаст независимые экземпляры жанров каждый раз, когда вы вызываете этот метод. При этом идентификаторы созданных сущностей будут равны нулю, EF будет рассматривать их как разные сущности. Е. Г.

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

во время сохранения изменений EF найдет два объекта в коллекции жанров. EF будет проверять идентификаторы сущностей и генерировать соответствующие SQL-запросы. Если id равен нулю,он будет генерировать запрос INSERT. Для ненулевого идентификатора 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" };

в классах вы связали исполнителя с жанром, а затем в коде вы добавили 2 жанра, используя поле Имя.

если бы вы сделали, вы бы остались в своем художнике и добавили 2

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

Я думаю, что есть две возможные причины, что не работает:

  1. вы совершали только один раз и в конце блока кода. Таким образом, EF добавляет все жанры как новый.

  2. базовый класс PersistenceEntity не может содержать Id имущества Key. И ваш Add метод принимает PersistenceEntity главный класс. Это может повлиять на весь объект как новый.