Полная вставка / обновление / удаление дочерних объектов в инфраструктуре Entity

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

public class QuestionModel { public int QuestionID { get; set; } public string QuestionText { get; set; } public IList Choices { get; set; } } public class QuestionChoiceModel { public int ChoiceID { get; set; } public string ChoiceText { get; set; } } 

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

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

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

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

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

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

Я прочитал много сообщений, таких как: Entity Framework не сохраняет измененных детей

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

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

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

Это сложная проблема. К сожалению, я не могу предложить решение, которое вы предпочитаете. Я не верю, что это возможно. 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(); } 

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

Контроллер имеет func UpdateModel, но он не будет работать с более сложной моделью, которая включала бы дочерние записи. Найдите TestUpdate .

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

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

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

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

 public class TestController : Controller where T : class { const string PK = "Id"; protected Models.Entities con; protected System.Data.Entity.DbSet model; public TestController() { con = new Models.Entities(); model = con.Set(); } // 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 для подтипов.