【问题标题】:Hydrate related objects水合相关对象
【发布时间】:2012-02-03 05:19:14
【问题描述】:

我正在寻找一种简单的方法来为相关对象补水。注释属于文档,只有文档的所有者才能添加注释,因此当用户尝试编辑注释时,我需要对相关文档进行水合以了解用户是否有权访问它。在我的服务层中,我有以下内容:

public void editNote(Note note)
    {
        // Get the associated Document object (required for validation) and validate.
        int docID = noteRepository.Find(note.NoteID).DocumentID;
        note.Document = documentRepository.Find(docID);

        IDictionary<string, string> errors = note.validate();
        if (errors.Count > 0)
        {
            throw new ValidationException(errors);
        }

        // Update Repository and save.
        noteRepository.InsertOrUpdate(note);
        noteRepository.Save();
    }

问题是,noteRepository.InsertOrUpdate(note) 抛出一个异常“ObjectStateManager 中已经存在具有相同键的对象”。当存储库设置 EntityState.Modified 时。因此出现了一些问题:

  1. 我是否正确地处理了这个问题,如果是,我该如何解决这个异常?
  2. 目前,控制器编辑操作采用 NoteCreateEditViewModel。现在它确实有一个 DocumentID 字段,因为在创建新注释时需要它,因为我们需要知道要将它附加到哪个文档。但是对于编辑,我不能使用它,因为恶意用户可以提供他们确实有权访问的 DocumentID,从而编辑他们不拥有的注释。那么是否应该有单独的视图模型用于创建和编辑,或者我可以在编辑时以某种方式排除 DocumentID?或者有没有更好的方法来处理不需要 ID 的视图模型?
  3. 有没有更好的方法来解决这个问题?我已经读过我应该只拥有一个文档存储库作为一个聚合并丢失注释存储库,但不确定这是否/如何帮助。

我问了一个与此相关的类似问题,但不是很清楚,所以希望这个版本能让别人理解,从而为我指明正确的方向。

编辑

根据 Ladislav Mrnka 提供的信息和此处详述的答案:An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key,看来我的存储库方法需要如下所示:

public void InsertOrUpdate(Note note)
    {
        if (note.NoteID == default(int)) {
            // New entity
            context.Notes.Add(note);
        } else {
            // Existing entity
            //context.Entry(note).State = EntityState.Modified;
            context.Entry(oldNote).CurrentValues.SetValues(note);
        }
    }

但是我如何从上下文中获取 oldNote 呢?我可以调用 context.Entry(Find(note.NoteID)).CurrentValues.SetValues(note) 但我是否在这里引入了潜在的问题?

【问题讨论】:

  • 是的 context.Find 方法是您正在寻找的,因为要使这项工作 oldNote 必须在更新之前加载,同时 Find 首先检查它是否已经加载并仅进行 db 查询如果在当前上下文中找不到它。

标签: asp.net-mvc asp.net-mvc-3 entity-framework poco


【解决方案1】:

我是否正确地处理了这个问题,如果是,我该如何解决这个异常?

我猜你的这部分代码会从数据库中加载整个 Node 来查找 DocumentID:

int docID = noteRepository.Find(note.NoteID).DocumentID;

在这种情况下,您的 InsertOrUpdate 无法获取您的节点并将其附加到具有已修改状态的上下文,因为您已经在上下文中拥有具有相同键的注释。常见的解决方案是使用这个:

objectContext.NoteSet.ApplyCurrentValues(note);
objectContext.SaveChanges();

但对于编辑,我不能使用它,因为恶意用户可以提供他们有权访问的 DocumentID,从而编辑他们不拥有的便笺。

在这种情况下,您必须添加一些安全性。您可以将任何数据添加到页面中的隐藏字段中,但那些不得由客户端更改的数据必须包含一些额外的安全性。例如,第二个隐藏字段具有在服务器上计算的签名或在服务器上计算的加盐值哈希。当数据在下一个请求中返回到服务器时,它必须重新计算并比较具有相同盐的签名/哈希,并验证传递的值和计算的值是否相同。确保客户端不知道您用于计算签名或哈希中使用的盐的秘密。

我已经读到我应该只拥有一个文档存储库作为一个聚合并丢失注释存储库,但我不确定这是否/如何帮助。

这是使用存储库的更简洁的方式,但它不会帮助您解决特定错误,因为您仍然需要 NoteDocumentId

【讨论】:

  • 你能给我举个 objectContext.NoteSet.ApplyCurrentValues(note) 的例子吗?不是 100% 确定我需要在这里做什么,因为我没有看到场景中可用的 ApplyCurrentValues 方法。
  • 你用的是什么版本的EF?
  • EF 4.0。我已经使用存储库方法编辑了我的问题。这是你的意思吗?
  • 那不是 EF 4.0。它是 EF 4.1 或 4.2(DbContext API)。
  • 哎呀,对不起。 4.2.它似乎可以正常工作,正确更新记录等,但对于调用 repo.Find 来获取旧注释有点不安。这种方法可以吗?或者有没有办法询问上下文给我旧的笔记?
猜你喜欢
  • 2016-11-20
  • 2012-05-13
  • 1970-01-01
  • 2019-03-09
  • 2021-04-08
  • 2019-01-12
  • 1970-01-01
  • 2011-02-19
  • 1970-01-01
相关资源
最近更新 更多