MVC3:存储库更新和ObjectStateManager

时间:2011-07-23 21:01:02

标签: entity-framework asp.net-mvc-3 repository 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);

在环顾四周看看我能做些什么来解决这个问题后,我偶然发现了this问题的答案。所以我通过用建议的代码替换我的Update方法来实现该答案。这也适用于我的管理方案,但不适用于TimesRead方案。抛出以下异常:

  

ObjectStateManager中已存在具有相同键的对象。 ObjectStateManager无法使用相同的键跟踪多个对象。

异常在其含义上非常清楚,但它确实让我想知道我应该如何处理这些简单的更新。我发现通过设置EntityState.Unchanged并更新TimesRead,我可以“欺骗”EF认为模型未更改,但会为管理员更新提供例外,说明ObjectStateManager没有持有对象的引用。

我也很清楚这些情景有何不同。 Edit操作将属性从ViewModel映射到新的,未连接的Article对象,而ArticleController正在处理直接从上下文检索的对象。这让我感觉我应该重构其中一个控制器方法,所以更新所采取的步骤是相同的​​。我只是不确定我应该如何处理,因为这两种方法似乎都应该能够与我共存。所以我的问题是,我可以更改哪些类型的更新才能正常工作?

感谢您的时间,我真的很抱歉发布的代码数量。我觉得这与问题都有关系。

1 个答案:

答案 0 :(得分:0)

两种方法的主要区别在于Admin Edit方法从您的AdminEditViewModel创建一个新文章,然后它将这个新创建的文章附加到您的数据库。这是有效的,因为它是一个从未连接到dc的新对象。

在第二种情况下,你从存储库中获取一篇文章,更新该文章,然后尝试再次附加它,这会失败,因为它不是新创建的文章,它是从数据库上下文返回的文章,所以它已经附加了。而你正试图再次附上它。