2014-10-14 9 views
5

Я знаю, что это было задано раньше, но после долгих часов поиска и кодирования я не могу подойти к рабочему и чистому подходу. Вот что у меня есть:Полная вставка/обновление/удаление дочерних объектов в платформе Entity

public class QuestionModel 
{ 
    public int QuestionID { get; set; } 
    public string QuestionText { get; set; } 

    public IList<QuestionChoiceModel> Choices { get; set; } 
} 

public class QuestionChoiceModel 
{ 
    public int ChoiceID { get; set; } 
    public string ChoiceText { get; set; } 
} 

Я использую EF 5 для этого приложения ASP.Net MVC. Общий шаблон репозитория и инъекция зависимостей с помощью Ninject с использованием InRequestScope() находятся на месте и работают плавно. Эти модели сопоставляются с объектами/без объектов.

Добавление новых вопросов к базе данных прямолинейно. Я задал свойство Question некоторых экземпляров QuestionChoice, а EF обрабатывает остальные.

Проблема об обновлениях. Предположим, у нас есть вопрос в базе данных с 3 QuestionChoices:

ChoiceID QuestionID ChoiceText 
-------- ---------- ---------- 
1   1    blah blah 
2   1    blah blah 
3   1    blah blah 

Когда страница редактирования Вопроса открывает (GET:/Вопросы/Редактировать/1), я показываю эти 3 варианта с использованием foreach в бритве. Я написал код JQuery, который добавляет или удаляет требуемую разметку для элементов ввода, если пользователь хочет. Таким образом, QuestionChoice с ID = 1 может быть отредактирован на клиенте, ID = 2 может быть удален, и новый ID = 4 может быть добавлен. Данные формы привязаны к QuestionModel отлично, когда пользователь нажимает кнопку «Сохранить» (POST:/Questions/Edit/1). Модель правильно отображается на объект вопроса. Вот где начинается история!

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

Я прочитал много постов как: Entity Framework not saving modified children

Я могу справиться изменения в этой грязной дороге. А также новые записи:

this._context.Entry(choice).State = EntityState.Added; 

Но я ищу более элегантный способ. А также обрабатывать записи, которые необходимо удалить. Есть ли хороший подход для обработки полного вставки/обновления/удаления дочерних объектов в этом сценарии с использованием EF? Честно говоря, я ожидал большего от EF.

ответ

6

Это сложная проблема. К сожалению, я не могу предложить решение, которое вы предпочитаете. Я не верю, что это возможно. EF не может отслеживать изменения, внесенные в ваши объекты, если они не сделаны в контексте, в котором объекты извлекаются, что невозможно в веб-среде. Единственным способом для этого было бы получить объект Question (внутри контекста) после POST в/Questions/Edit/1 и выполнить тип «слияния» между POSTed Question и Вопросом, полученным из базы данных. Это будет включать назначение свойств на вашем QuestionModel и каждый QuestionChoiceModel, извлеченный из базы данных, используя ваш POSTed QuestionModel. Я скажу, что это тоже не будет большой практикой, потому что вы забудете включить собственность. Это произойдет.

Лучшее (и самое простое) решение, которое я могу предоставить, это добавить/отредактировать QuestionModel и QuestionChoiceModel(s), используя метод .Entry() выше. Вы пожертвуете «лучшей практикой» здесь для решения, которое будет менее подверженным ошибкам.

QuestionModel questionFromDb; 
QuestionModel questionFromPost; 

QuestionModelChoice[] deletedChoices = questionFromDb.Choices.Where(c => !questionFromPost.Choices.Any(c2 => c2.Id == c.Id)); 


using (var db = new DbContext()) 
{ 
    db.Entry(questionFromPost).State = questionFromPost.Id == 0 ? EntityState.Added : EntityState.Modified; 

    foreach(var choice in questionFromPost.Choices) 
    { 
     db.Entry(choice).State = choice.Id == 0 ? EntityState.Added : EntityState.Modified; 
    } 

    foreach(var deletedChoice in deletedChoices) 
    { 
     db.Entry(deletedChoice).State = EntityState.Deleted; 
    } 

    db.SaveChanges(); 
} 
+0

Спасибо. Я буду смотреть в него. –

0

Это просто доказательство концепции.

Controler имеют FUNC UpdateModel но это не будет работать с более сложной модели, которые включены дочерние записи. Ищите TestUpdate.

Правило № 1: Каждая таблица имеет столбец идентификатора PK.

Правило № 2: Каждый FK должен быть установлен.

Правило № 3: Каскадное удаление необходимо установить. если вы хотите удалить соответствующую запись.

Правило № 4: Новая запись должна иметь Id = 0 или лучше будет Нулевой, но Id can not be null.

public class TestController<T> : Controller where T : class 
{ 
    const string PK = "Id"; 

    protected Models.Entities con; 
    protected System.Data.Entity.DbSet<T> model; 
    public TestController() 
    { 
     con = new Models.Entities(); 
     model = con.Set<T>(); 
    } 

    // GET: Default 
    public virtual ActionResult Index() 
    { 
     ViewBag.Result = TempData["Result"]; 
     TempData["Result"] = null; 

     var list = model.ToList(); 
     return View(list); 
    } 

    [HttpGet] 
    public virtual ActionResult AddEdit(string id) 
    { 
     int nId = 0; 
     int.TryParse(id, out nId); 

     var item = model.Find(nId); 
     return View(item); 

    } 

    [HttpPost] 
    public virtual ActionResult AddEdit(T item) 
    { 
     TestUpdate(item); 

     con.SaveChanges(); 

     return RedirectToAction("Index"); 
    } 

    [HttpGet] 
    public virtual ActionResult Remove(string id) 
    { 
     int nId = 0; 
     int.TryParse(id, out nId); 
     if (nId != 0) 
     { 
      var item = model.Find(nId); 
      con.Entry(item).State = System.Data.Entity.EntityState.Deleted; 
      con.SaveChanges(); 
     } 
     return Redirect(Request.UrlReferrer.ToString()); 
    } 

    private void TestUpdate(object item) 
    { 
     var props = item.GetType().GetProperties(); 
     foreach (var prop in props) 
     { 
      object value = prop.GetValue(item); 
      if (prop.PropertyType.IsInterface && value != null) 
      { 
       foreach (var iItem in (System.Collections.IEnumerable)value) 
       { 
        TestUpdate(iItem); 
       } 
      } 
     } 

     int id = (int)item.GetType().GetProperty(PK).GetValue(item); 
     if (id == 0) 
     { 
      con.Entry(item).State = System.Data.Entity.EntityState.Added; 
     } 
     else 
     { 
      con.Entry(item).State = System.Data.Entity.EntityState.Modified; 
     } 

    } 

} 

Вот проект https://github.com/mertuarez/AspMVC_EF/

Вам нужно создать Controler для модели и создать представление для действия. В случае действия AddEdit вам нужно создать редактор Template для подтипов.

Смежные вопросы