Удаление элемента из коллекции в EF - странная проблема - PullRequest
0 голосов
/ 07 января 2019

Я работаю с проектом Entity Framework, где я пытаюсь удалить Предмет из одной из моих коллекций. Сначала я создал отношение «Один ко многим» между моими объектами «Resto» и «OpeningTimes» следующим образом: В моей модели:

 [Table("Resto")]
public class Resto
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string PhoneNumber { get; set; }
    public string Address { get; set; }   //TODO Ajouter adresse détaillées 

    public virtual ICollection<SlotTime> OpeningTimes {get; set;}




    public virtual ICollection<ApplicationUser> Administrators { get; set; }
    public virtual ICollection<ApplicationUser> Chefs { get; set; }

    public virtual Menu Menu {get; set;}
}
[Table("RestoSlotTimes")]

public class SlotTime
{
    public int SlotTimeId { get; set; }
    public DayOfWeek DayOfWeek { get; set; }
    public TimeSpan OpenTime { get; set; }
    public TimeSpan CloseTime { get; set; }


    public int RestoId { get; set; }
    public virtual Resto Resto { get; set; }
}

Поскольку у меня есть другое отношение к этому объекту (см. Раздел «Пользователь приложения»), я также использую язык Fluent для устранения неоднозначностей.

следующим образом:

        modelBuilder.Entity<Resto>()
                    .HasMany<ApplicationUser>(s => s.Administrators)
                    .WithMany(c => c.Resto_Admin)
                    .Map(cs =>
                    {
                        cs.MapLeftKey("Resto_Admin");
                        cs.MapRightKey("Admin");
                        cs.ToTable("RestosAdmins");
                    });

        modelBuilder.Entity<Resto>()
        .HasMany<ApplicationUser>(s => s.Chefs)
        .WithMany(c => c.Resto_Chefs)
        .Map(cs =>
        {
            cs.MapLeftKey("Resto_Chefs");
            cs.MapRightKey("Chef");
            cs.ToTable("RestosChefs");
        });
modelBuilder.Entity<Resto>()
                    .HasOptional(s => s.Menu) 
                    .WithRequired(ad => ad.resto)
                    .WillCascadeOnDelete(true); 

Но эти отношения работают нормально.

Сегодня я выполняю основные операции с моим «OpeningTimes» в моем контроллере, добавляя элемент в БД со следующим:

        [HttpPost]
    public async Task<ActionResult> AddSlotTimeToRestaurant(AddSlotTimeToRestaurantView model)
    {
        var resto = await DbManager.Restos.FirstAsync(r => r.Id == model.RestoId);
        if(resto != null)
        {
            if(model.OpenTimeId < model.CloseTimeId)
            {
                SlotTime slotTime = new SlotTime()
                {
                    DayOfWeek = model.Day,
                    OpenTime = model.SlotTimeList.timeSpanViews.FirstOrDefault(m => m.Id == model.OpenTimeId).TimeSpan,
                    CloseTime = model.SlotTimeList.timeSpanViews.FirstOrDefault(m => m.Id == model.CloseTimeId).TimeSpan
                };
                resto.OpeningTimes.Add(slotTime);
                await DbManager.SaveChangesAsync();
                return RedirectToAction("edit", new { id = model.RestoId });
            }
            else
            {
                ModelState.AddModelError("SelectedSlotTimeId_1_Stop", "L'heure de fermeture doit être après l'heure d'ouverture");
                return View();
            }               
        }
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }

Этот код работает должным образом. Но теперь я пытаюсь сделать другую функцию для удаления объекта с помощью следующего кода:

   public async Task<ActionResult> RemoveSlotTimeToRestaurant(int RestoId, int SlotId)
    {
        var resto = await DbManager.Restos.FirstAsync(r => r.Id == RestoId);
        if (resto != null)
        {
            SlotTime slotTime = resto.OpeningTimes.FirstOrDefault(m => m.SlotTimeId == SlotId);

            if(slotTime != null)
            {
                resto.OpeningTimes.Remove(slotTime);
                await DbManager.SaveChangesAsync();
                return RedirectToAction("edit", new { id = RestoId });
            }
        }
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }

У меня ошибка в строке DbSave ... Там, где выглядит, как EF, старайтесь не удалять мой объект, а просто сохранить его и установить для его идентификатора значение Null ... Это не то, что я ожидаю

Операция завершилась неудачно: отношение не может быть изменено, поскольку одно или несколько свойств внешнего ключа не могут быть равны нулю. Когда в отношение вносится изменение, для соответствующего свойства внешнего ключа устанавливается нулевое значение. Если внешний ключ не поддерживает нулевые значения, необходимо определить новое отношение, свойству внешнего ключа должно быть присвоено другое ненулевое значение или несвязанный объект должен быть удален

У меня такое чувство, что мои отношения настроены для EF. Я чувствую, что делаю отношения добавления и удаления правильно ... Как вы думаете, это может происходить из беглой конфигурации?

Кстати, когда я удаляю «Resto», у меня хорошо работает каскадное удаление, где все мои элементы «OpeningTimes» удаляются из БД без ошибок.

С уважением,

Ответы [ 4 ]

0 голосов
/ 07 января 2019

Когда вы удаляете slotTime, есть ли другая таблица, у которой есть внешний ключ, связанный с таблицей OpeningTimes? Похоже, что вы удаляете элемент, и соответствующая таблица пытается установить для внешнего ключа значение null, в котором это не разрешено. Необходимо убедиться, что таблицы, связанные с OpeningTimes, изменены соответствующим образом, чтобы учесть удаление записи.

Редактировать: Другие ответы более подробны, и они намного лучше, чем мои.

0 голосов
/ 07 января 2019

Если вы хотите иметь возможность изменять дочернюю сущность (OpeningTimes) через родительскую сущность (Resto), то вам нужно указать взаимосвязь между ними в построителе модели:

modelBuilder.Entity<Resto>()
    .HasMany(r => r.OpeningTimes) 
    .WithOptional()
    .HasForeignKey(ot => ot.RestoId)

В этом блоге более подробно рассматривается обработка этого сценария.


Другой подход заключается в том, чтобы просто удалить сущность непосредственно из DbSet, определенного в DbContext:

DbManager.SlotTimes.Remove(slotTime);
0 голосов
/ 07 января 2019

Мне кажется, что вам нужно добавить еще один свободный вызов, чтобы определить, как SlotTimes относится к Resto.

Из того, что вы описываете, SlotTime полностью зависит от Resto: у вас не может быть SlotTime, который не связан с Resto. Но, очевидно, EF не может просто заключить это: вам придется сказать это, с быстрой конфигурацией.

Я не могу восстановить это из памяти, потому что это слишком сложно, и я не на машине с установленной VS. Но я думаю, вы бы начали с этого:

  modelBuilder.Entity<Resto>()
    .HasMany<TimeSlot>(r => r.OpeningTimes)
    .WithOne(t => t.Resto)
    ....

Я прошу прощения за то, что не смог выполнить весь вызов для вас.

Кстати, у этого другого вопроса есть очень хороший ответ, который объясняет все это: EF 4: удаление дочернего объекта из коллекции не удаляет его - почему?

0 голосов
/ 07 января 2019

Можете ли вы попробовать, как показано ниже:

DbManager.Entry(slotTime).State = EntityState.Deleted;
await DbManager.SaveChangesAsync();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...