Entity Framework 4 не сохраняет мои строки «многие ко многим» - PullRequest
12 голосов
/ 01 марта 2012

Надеюсь, очень простой вопрос. Но я использую Code First с приложением MVC, и у меня есть объект Category и ServiceType, который имеет отношение многие ко многим:

public class Category
{
    public Category()
    {
        ServiceTypes = new HashSet<ServiceType>();
    }

    public Guid CategoryId { get; set; }

    [Required(ErrorMessage = "Name is required")]
    public string Name { get; set; }

    public virtual ICollection<ServiceType> ServiceTypes { get; set; }
}

База данных была сгенерирована правильно и содержит таблицу ссылок с именем CategoryServiceTypes. Моя проблема заключается в том, что я добавляю элементы в свою коллекцию ServiceTypes и вызываю save. Несмотря на отсутствие ошибок, строки не добавляются в CategoryServiceTypes. Когда приведенный ниже код попадает в SaveChanges, счетчик category.ServiceTypes равен 1, поэтому что-то определенно находится в коллекции:

    [HttpPost]
    public ActionResult Edit(Category category, Guid[] serviceTypeIds)
    {
        if (ModelState.IsValid)
        {
            // Clear existing ServiceTypes
            category.ServiceTypes.Clear();

            // Add checked ServiceTypes
            foreach (Guid serviceType in serviceTypeIds)
            {
                ServiceType st = db.ServiceTypes.Find(serviceType);
                category.ServiceTypes.Add(st);
            }

            db.Entry(category).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(category);
    }

Я надеюсь, что я делаю что-то явно не так здесь. Есть идеи?

Спасибо.

EDIT:

Хотя приведенный ниже ответ действительно является правильным ответом, я подумал, что добавлю следующую окончательную версию метода редактирования сообщения:

    [HttpPost]
    public ActionResult Edit(Category category, Guid[] serviceTypeIds)
    {
        if (ModelState.IsValid)
        {
            // Must set to modified or adding child records does not set to modified
            db.Entry(category).State = EntityState.Modified;

            // Force loading of ServiceTypes collection due to lazy loading
            db.Entry(category).Collection(st => st.ServiceTypes).Load(); 

            // Clear existing ServiceTypes
            category.ServiceTypes.Clear();

            // Set checked ServiceTypes
            if (serviceTypeIds != null)
            {
                foreach (Guid serviceType in serviceTypeIds)
                {
                    ServiceType st = db.ServiceTypes.Find(serviceType);
                    category.ServiceTypes.Add(st);
                }
            }

            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(category);
    }

Обратите внимание на строку, которая вызывает загрузку коллекции ServiceTypes, это необходимо, поскольку отложенная загрузка не включает эти дочерние элементы, что означает, что очистка коллекции ServiceTypes ничего не делала.

1 Ответ

8 голосов
/ 01 марта 2012

Попробуйте переместить строку, где вы прикрепляете категорию к контексту перед циклом:

[HttpPost]
public ActionResult Edit(Category category, Guid[] serviceTypeIds)
{
    if (ModelState.IsValid)
    {
        // Clear existing ServiceTypes
        category.ServiceTypes.Clear();
        db.Entry(category).State = EntityState.Modified;
        // category attached now, state Modified

        // Add checked ServiceTypes
        foreach (Guid serviceType in serviceTypeIds)
        {
            ServiceType st = db.ServiceTypes.Find(serviceType);
            // st attached now, state Unchanged
            category.ServiceTypes.Add(st);
            // EF will detect this as a change of category and create SQL
            // to add rows to the link table when you call SaveChanges
        }

        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(category);
}

В вашем коде EF не замечает, что вы добавили типы обслуживания, потому что вы присоединяете категорию к контексту, когда типы обслуживания уже находятся в коллекции category.ServiceTypes и все типы обслуживания уже присоединены к контексту в состоянии Unchanged .

...