0

我的存储库中有一个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);

在环顾四周,看看我能做些什么来解决这个问题后,我发现了这个SO question 的答案。Update所以我通过用建议的代码替换我的方法来实现这个答案。这在我的管理场景中也可以正常工作,但在场景中却不行TimesRead。抛出以下异常:

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

例外的含义很清楚,但它确实让我想知道我应该如何处理这些简单的更新。我发现我可以“愚弄” EF 认为模型通过设置没有改变,EntityState.Unchanged并且会更新TimesRead,但会为管理员更新提供例外,说明ObjectStateManager不包含对对象的引用。

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

感谢您的宝贵时间,对于发布的大量代码,我感到非常抱歉。我只是觉得这一切都与问题有关。

4

1 回答 1

0

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

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

于 2011-07-23T22:08:18.337 回答