Entity Framework Core: добавление новой записи в таблицу «объединение» удаляет существующие записи

Часть кода удалена для краткости.

Я создал веб-сайт ASP.NET Core MVC, используя Entity Framework Core.

У меня есть три таблицы для Parts, Distributors и DistributorParts (таблица соединений). На моем сайте есть страница для добавления Distributors к Part. Когда я создаю новую запись DistributorPart для Распространителя 1 и Части 1, запись вставляется правильно. Но когда я добавляю вторую запись DistributorPart для Распространителя 2 и Части 1, первая запись DistributorPart удаляется! (обратите внимание, что две вышеупомянутые записи DistributorPart используют Часть 1)

Мои вопросы:

  • Как я могу вставить новые DistributorPart записей, без удаления/изменения существующих записей?
  • Что я делаю не так?

Модели:

public class Part
{
    [key]
    public int Id { get; set; }

    public string Description { get; set; }

    public ICollection<DistributorPart> DistributorParts { get; set; }
}

public class Distributor
{
    [key]
    public int Id { get; set; }

    [Required]
    public string Name { get; set; }

    public ICollection<DistributorPart> DistributorParts { get; set; }
}

public class DistributorPart
{
    [key]
    public int Id { get; set; }

    public int OnHand { get; set; }

    public int OnOrder { get; set; }

    public int PartId { get; set; }

    public Part Part { get; set; }

    public int DistributorId { get; set; }

    public Distributor Distributor { get; set; }
}

Вот мой DbContext:

public class MyContext : DbContext
{
    public DbSet<Part> Parts { get; set; }

    public DbSet<Distributor> Distributors { get; set; }

    public DbSet<DistributorPart> DistributorParts { get; set; }

    public MyContext(DbContextOptions<MyContext> options) 
        : base(options) 
    { }
}

Вот код, который вставляет новую запись DistributorPart:

public DistributorPart AddDistributorPart(DistributorPart distributorPart)
{
    var newDistributorPart = new DistributorPart
    {
        OnHand = distributorPart.OnHand,
        OnOrder = distributorPart.OnOrder,
        PartId = distributorPart.PartId,
        DistributorId = distributorPart.DistributorId
    };

    _context.Add(newDistributorPart);
    _context.SaveChanges();

    return newDistributorPart;
}

Я могу проверить это в окне вывода приложения при отладке:

Microsoft.EntityFrameworkCore.Database.Command:Информация: Выполнена команда DbCommand (1 мс) [Параметры=[@p0='?', @p1='?', @p2='?'], CommandType='Text', CommandTimeout=' 30'] УДАЛИТЬ ИЗ "DistributorParts", ГДЕ "Id" = @p0; ...

ИЗМЕНИТЬ:

Вот мой метод Controller, который вызывает метод AddDistributorPart():

public DistributorPart AddDistributorToPart([FromBody] DistributorPart distributorPart)
{
    var part = _partService.GetPart(distributorPart.PartId);
    if (part == null || part.Id <= 0)
    {
        throw new Exception($"Part Id of {distributorPart.PartId} does not exist.");
    }

    var distributor = _distributorService.GetDistributor(distributorPart.DistributorId);
    if (distributor == null || distributor.Id <= 0)
    {
        throw new Exception($"Distributor Id of {distributorPart.DistributorId} does not exist.");
    }

    var userId = GetUserId();
    var newDistributorPart = _distributorPartService.AddDistributorPart(distributorPart, userId);

    ...

    return newDistributorPart;
}

person Dan    schedule 19.05.2018    source источник
comment
Ваш код правильный. Что вы видите, когда проверяете таблицу DistributorParts? Строки на самом деле в этой таблице?   -  person CodeNotFound    schedule 20.05.2018
comment
Не могу воспроизвести. В посте должно быть что-то не показано.   -  person Ivan Stoev    schedule 20.05.2018
comment
@CodeNotFound, спасибо! Я просто удалил все данные в таблице DistParts, чтобы начать заново. Когда я добавляю новую запись в таблицу DistributorPart, у меня есть идентификатор 15, partId 1, DistributorId 1. Когда я добавляю еще одну запись для PartId 1 и DistributorId 2, предыдущая запись с идентификатором 15 удаляется из базы данных, а идентификатор 16 на своем месте (появляется только одна запись, увеличенный ПК). Какого черта!   -  person Dan    schedule 20.05.2018
comment
@IvanStoev, спасибо! Думаю, вы правы, но я не могу понять, что происходит. Из того, что я исследовал, я думаю, что мои миграции верны при настройке таблицы, поскольку отношения кажутся правильными, но EF удаляет записи, как показано в выходных данных приложения выше. Могу ли я предоставить какой-либо другой код, который, по вашему мнению, может быть полезен?   -  person Dan    schedule 20.05.2018
comment
Поскольку переменная _context, по-видимому, совместно используется разными вызовами (и даже разными репозиториями или любыми вспомогательными классами, которые вы используете, трудно сказать, где искать. Можете ли вы поставить точку останова перед строкой _context.Add(newDistributorPart); и проверить var deletedEntries = _context.ChangeTracker.Entries().Where(e => e.Entity is DistributorPart && e.State == EntityState.Deleted).ToList(); и посмотреть, пуста ли она?   -  person Ivan Stoev    schedule 20.05.2018
comment
@IvanStoev, да, этот список пуст. Я очистил окно вывода приложения и все еще вижу, как происходит удаление (я вставляю новую запись в другую таблицу, для краткости не упомянутую выше, называемую InternalPartNumber): [40m[32minfo[39m[22m[49m: Microsoft. EntityFrameworkCore.Database.Command[20101] Выполнена команда DbCommand (15 мс) [Параметры=[@p0='?', @p1='?', @p2='?'], CommandType='Text', CommandTimeout='30' ] УДАЛИТЬ ИЗ DistributorParts WHERE Id = @p0; INSERT INTO InternalPartNumbers (Created, Description) VALUES (@p1, @p2) RETURNING Id;   -  person Dan    schedule 20.05.2018
comment
@IvanStoev, я удалил фильтрацию по State == Deleted и вижу две записи для DistributorPart. Первая запись — это существующая запись в базе данных со статусом Modified (хотя я ее вообще не изменяю). Вторая запись — новая, которую я сохраняю в БД с состоянием «Добавлено». Почему должно быть две записи, когда я просто добавляю новую?   -  person Dan    schedule 20.05.2018
comment
В EF важна каждая мелочь. Я бы посоветовал вам предоставить урезанную, но воспроизводимую модель примера и код. Если вы создадите новый чистый проект только с информацией, предоставленной в сообщении, вы увидите, что этого не происходит.   -  person Ivan Stoev    schedule 20.05.2018
comment
Относительно вашего последнего ответа: я хотел увидеть состояние средства отслеживания изменений до вызова Add, поэтому не должно быть сущности с состоянием Added. И никаких Modified или Deleted. Затем сделайте то же самое после Add вызова. Просто пытаюсь выяснить, является ли метод Add причиной странных вещей или есть что-то до этого :)   -  person Ivan Stoev    schedule 20.05.2018
comment
@IvanStoev, извините, только что понял, что поставил этот трекер после добавления, но перед .SaveChanges(). Я только что добавил трекер в первую строку метода AddDistributorPart(), и у него есть запись в списке со статусом Modified. Это действительно странно... Я собираюсь добавить этот трекер в метод PartsService.GetPart(), вызываемый из конструктора (вставил этот код Ctor как редактирование этого поста).   -  person Dan    schedule 20.05.2018
comment
Нп. Вот что вы можете сделать. Возьмите мой ответ на ObjectStateManager.ObjectStateManagerChanged в Entity Framework Core. Внутри OnStateManagerChanged добавьте точку останова с условием entry.Entity is DistributorPart && entry.EntityState == EntityState.Modified. При достижении точки останова изучите стек вызовов, чтобы увидеть, какой код вызывает это.   -  person Ivan Stoev    schedule 21.05.2018
comment
Святая корова! Кажется, я понял, что происходит! В этом PartsService.GetPart() код, который извлекает части, выглядит следующим образом: var part = _context.Parts.Where(p => p.Id == id).Include(p => p.DistributorParts).FirstOrDefault(); Я добавил 'Include()', чтобы он заполнил коллекцию/список DistributorParts. Когда я комментирую .Include(), он работает, как и ожидалось. Но с .Include() код отслеживания говорит, что DistributorParts находится в состоянии «Изменено». Есть идеи, что происходит, @IvanStoev? Спасибо еще раз!!!   -  person Dan    schedule 22.05.2018
comment
За исключением случаев, когда есть какая-то ошибка EF Core, о которой я не знаю, Include не может переводить объект в измененное состояние. Я полагаю, что это происходит из-за какой-то другой операции. Попробуйте предложение из моего предыдущего комментария, стек вызовов действительно должен дать вам информацию о том, какая операция вызывает это.   -  person Ivan Stoev    schedule 22.05.2018