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.

Во втором случае вы получаете Статью из репозитория, обновляете эту Статью, затем пытаетесь присоединить ее снова, это терпит неудачу, потому что это не вновь созданная Статья, это статья, возвращенная из контекста БД в первую очередь, поэтому она уже присоединена, и вы пытаетесь прикрепить его снова.

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