Удаление элемента из коллекции в EF - странная проблема

Я работаю с проектом фреймворка сущностей, в котором мне трудно удалить элемент из одной из моих коллекций. Сначала у меня есть отношение «Один ко многим», созданное между моим объектом «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; }
}

Поскольку у меня есть другое отношение к этому объекту (см. Одно из приложений User), я также использую язык 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 ... Это не то, что я ожидал

The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted

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

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

С наилучшими пожеланиями,

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
91
4

Ответы 4

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

DbManager.Entry(slotTime).State = EntityState.Deleted;
await DbManager.SaveChangesAsync();

Здравствуйте, да, так это работает ... Похоже, EF не понимает "Удалить" как действие "удалить" ;-)

Jonathan Halleux 07.01.2019 17:13

EF будет понимать remove как удаление этой строки и не будет учитывать правило каскада, поэтому он будет иметь значение null в родительской таблице, удалив запись дочерней таблицы. Если каскадное правило установлено правильно и вы хотите удалить средства, основанные на каскадном правиле, вам необходимо указать приведенные выше инструкции. Это применимо к сценариям отношений «один ко многим».

sri harsha 07.01.2019 17:22

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

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

Это сообщение в блоге более подробно описывает обработку этого сценария.


Другой подход - просто удалить объект непосредственно из DbSet, определенного в DbContext:

DbManager.SlotTimes.Remove(slotTime);

Привет, devNull. Я пробовал, как вы предложили, плавный подход, но проблема все та же ... То же сообщение об ошибке.

Jonathan Halleux 07.01.2019 21:12

И для второго подхода при этом я не указал slotTime в DbSet. Я получаю к нему доступ только через Resto из его Childs ...

Jonathan Halleux 07.01.2019 21:13

Да, наконец, ваш второй подход кажется правильным. Наконец нашел вот это: weblogs.asp.net/zeeshanhirani/…

Jonathan Halleux 07.01.2019 21:25

Кстати, как я уже сказал выше, "OpeningTimes" недоступен через мой DbManager ... Я верю, потому что noe установлен в DBSet ... Должен ли я объявить его там? Это лучший способ объявить Чайлдс в DbSet?

Jonathan Halleux 07.01.2019 21:34

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

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

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

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

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

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

Спасибо, Энн, действительно, твой подход не сработает (как у devNull). Но вы можете найти ответ дальше по присланной вами ссылке. Здесь доступно полное объяснение weblogs.asp.net/zeeshanhirani/…

Jonathan Halleux 07.01.2019 21:23

@JonathanHalleux Рад, что вы нашли ответ!

Ann L. 08.01.2019 14:32

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

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

Другие вопросы по теме