MVC3: Обновления репозитория и ObjectStateManager
У меня есть Update
метод в моем репозитории, который я использую для обновления статей о моем проекте. Первоначально я использовал этот метод только для редактирования прав администратора для статей. Он справляется с этим правильно, но я решил добавить простой механизм для вычисления "самых читаемых" статей. Для этого я хотел бы обновить TimesRead
собственность каждый раз, когда статья была просмотрена. Это доставляет мне проблемы с обновлениями, которые, кажется, вращаются вокруг использования ObjectStateManager.ChangeObjectState
, Вот мой Update
метод:
public void Update(Article article)
{
if (article == null) return;
db.Articles.Attach(article);
db.ObjectStateManager.ChangeObjectState(article, EntityState.Modified);
db.SaveChanges();
}
В моем AdminController
следующий метод корректно обновляется:
[HttpPost]
public ActionResult Edit(AdminEditViewModel viewModel)
{
if (ModelState.IsValid)
{
Article article = Mapper.Map<AdminEditViewModel, Article>(viewModel);
articleRepository.Update(article);
return RedirectToAction("Index");
}
viewModel.Categories = new SelectList(categoryRepository.GetAll(), "CategoryID", "Name", viewModel.CategoryID);
return View(viewModel);
}
Однако в TimesRead
В этом случае обновление вызовет исключение:
Объект не может быть присоединен, потому что он уже находится в контексте объекта. Объект может быть присоединен только тогда, когда он находится в неизменном состоянии.
Соответствующий код из этого метода контроллера:
var model = articleRepository.GetByID(id);
model.TimesRead++;
articleRepository.Update(model);
return View(model);
Осмотревшись вокруг, чтобы увидеть, что я могу сделать, чтобы решить эту проблему, я наткнулся на ответ на этот ТАК вопрос. Поэтому я реализовал этот ответ, заменив мой Update
метод с предложенным кодом. Это также работает правильно в моем сценарии администратора, но не в TimesRead
сценарий. Выдается следующее исключение:
Объект с таким же ключом уже существует в ObjectStateManager. ObjectStateManager не может отслеживать несколько объектов с одним и тем же ключом.
Исключения достаточно ясны по своему значению, но это заставляет меня задуматься, как мне поступить с такими простыми обновлениями, как эти. Я обнаружил, что могу "обмануть" EF, думая, что модель не изменилась, установив EntityState.Unchanged
и это обновит TimesRead
но дать исключение для обновлений администратора, заявив ObjectStateManager
не содержит ссылку на объект.
Мне также ясно, как эти сценарии отличаются. Edit
действие сопоставляет свойства из ViewModel на новый, неприкрепленный Article
объект, тогда как, ArticleController
имеет дело с объектом, полученным непосредственно из контекста. Это оставляет у меня ощущение, что я должен рефакторинг одного из этих методов контроллера, чтобы шаги, предпринятые для обновления, были такими же. Я просто не совсем уверен, как я должен подходить к этому, поскольку оба подхода кажутся способными сосуществовать со мной. Итак, мой вопрос: что я могу изменить, чтобы оба типа обновлений работали правильно?
Спасибо за ваше время и мне очень жаль за количество размещенного кода. Я просто чувствую, что все это имеет отношение к проблеме.
1 ответ
Основное различие между вашими двумя методами заключается в том, что администратор Edit
Метод создает новую Статью из вашей AdminEditViewModel, затем он присоединяет эту вновь созданную Статью к вашей базе данных. Это работает, потому что это новый объект, который никогда не был присоединен к DC.
Во втором случае вы получаете Статью из репозитория, обновляете эту Статью, затем пытаетесь присоединить ее снова, это терпит неудачу, потому что это не вновь созданная Статья, это статья, возвращенная из контекста БД в первую очередь, поэтому она уже присоединена, и вы пытаетесь прикрепить его снова.