我的存储库中有一个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);
在环顾四周看看我能做些什么来解决这个问题后,我偶然发现了this问题的答案。所以我通过用建议的代码替换我的Update
方法来实现该答案。这也适用于我的管理方案,但不适用于TimesRead
方案。抛出以下异常:
ObjectStateManager中已存在具有相同键的对象。 ObjectStateManager无法使用相同的键跟踪多个对象。
异常在其含义上非常清楚,但它确实让我想知道我应该如何处理这些简单的更新。我发现通过设置EntityState.Unchanged
并更新TimesRead
,我可以“欺骗”EF认为模型未更改,但会为管理员更新提供例外,说明ObjectStateManager
没有持有对象的引用。
我也很清楚这些情景有何不同。 Edit
操作将属性从ViewModel映射到新的,未连接的Article
对象,而ArticleController
正在处理直接从上下文检索的对象。这让我感觉我应该重构其中一个控制器方法,所以更新所采取的步骤是相同的。我只是不确定我应该如何处理,因为这两种方法似乎都应该能够与我共存。所以我的问题是,我可以更改哪些类型的更新才能正常工作?
感谢您的时间,我真的很抱歉发布的代码数量。我觉得这与问题都有关系。
答案 0 :(得分:0)
两种方法的主要区别在于Admin Edit
方法从您的AdminEditViewModel创建一个新文章,然后它将这个新创建的文章附加到您的数据库。这是有效的,因为它是一个从未连接到dc的新对象。
在第二种情况下,你从存储库中获取一篇文章,更新该文章,然后尝试再次附加它,这会失败,因为它不是新创建的文章,它是从数据库上下文返回的文章,所以它已经附加了。而你正试图再次附上它。