2011-05-16 67 views
3

我检查了this question,它似乎与我所需要的有关,但并未完全回答它。ASP.NET MVC3代码优先错误 - 尝试更新实体

我有一个实体(Sql Compact,使用EF Code First,通过MVC3--如果标题中没有明确的话),用于“Issue”(通用问题跟踪,仅用于我自己对MVC3工作原理的理解)。 Issue类有一个CreatedBy属性(Int引用创建问题的用户)和一个CreatedDate属性(DateTime)。当我使用脚手架代码来更新(仅修改,以防止某些更新的日期字段从被用户修改):

 if (ModelState.IsValid) 
     { 
      issue.LastActivity = (DateTime?)DateTime.Now.Date; 
      if (issue.ClosedBy != null) issue.ClosedDate = (DateTime?)DateTime.Now.Date; 
      startingIssue = null; 
      db.Entry(issue).State = EntityState.Modified; 
      db.SaveChanges(); 
      return RedirectToAction("Index"); 
     } 

我接收在链接的问题中提到一个DATETIME2数据类型的(转换到错误日期时间数据类型等等)

当我逐句通过代码时,看起来我的CreatedBy和CreatedDate属性不包含在控制器正在传递的issue实例中。当我尝试通过从DB抓住了问题的另一个副本,并更新那些值来解决这个问题:

 var startingIssue = db.Issues.Find(issue.IssueId); 
     if (ModelState.IsValid) 
     { 
      if (issue.CreatedBy != startingIssue.CreatedBy) issue.CreatedBy = startingIssue.CreatedBy; 
      if (issue.CreatedDate != startingIssue.CreatedDate) issue.CreatedDate = startingIssue.CreatedDate; 
      issue.LastActivity = (DateTime?)DateTime.Now.Date; 
      if (issue.ClosedBy != null) issue.ClosedDate = (DateTime?)DateTime.Now.Date; 
      startingIssue = null; 
      db.Entry(issue).State = EntityState.Modified; 
      db.SaveChanges(); 
      return RedirectToAction("Index"); 
     } 

我得到的并发冲突:具有相同键的对象已经存在于ObjectStateManager。 ObjectStateManager不能使用同一个键跟踪多个对象。

那么,如何在不违反并发的情况下,如何让EF查看已经在数据库中设置的日期(因此它不会尝试将CreatedDate更新为1/1/0001)?

编辑 好的...我找到了。显然,我在寻找@Html.HiddenFor(model => model.[property]),并将编辑器添加到视图中。这对我来说似乎有点愚蠢,但它的确行得通,无需添加自定义代码来分离一个对象并替换更新的对象。

回答

15

简短的回答是,您已经将实体加载到Find的上下文中,并且您以后不能再附加另一个实体。

你有两种选择:

  • 拆离的第一个实例,然后装上第二
  • 复制从第二个实例字段添加到第一

我将分享代码为第一个选项。设置变量的

public void Detach(object entity) 
{ 
    var objectContext = ((IObjectContextAdapter)this).ObjectContext; 
    objectContext.Detach(entity); 
} 

然后调用Detach而不是null

var startingIssue = db.Issues.Find(issue.IssueId); 
if (ModelState.IsValid) 
{ 
    if (issue.CreatedBy != startingIssue.CreatedBy) issue.CreatedBy = startingIssue.CreatedBy; 
    if (issue.CreatedDate != startingIssue.CreatedDate) issue.CreatedDate = startingIssue.CreatedDate; 
    issue.LastActivity = (DateTime?)DateTime.Now.Date; 
    if (issue.ClosedBy != null) issue.ClosedDate = (DateTime?)DateTime.Now.Date; 

    // startingIssue = null; 
    db.Detach(startingIssue); 

    db.Entry(issue).State = EntityState.Modified; 
    db.SaveChanges(); 
    return RedirectToAction("Index"); 
} 
+0

这是有效的。谢谢。我会等待看看是否有其他人碰巧提供了更优雅的解决方案,但是如果没有,我会选择你的答案作为接受的答案。 – AllenG 2011-05-16 19:53:33

+0

看到我的更新 - 无论如何给你点。 – AllenG 2011-05-16 21:00:27

+1

我有类似的问题。我在创建新记录时需要设置我的表(CreatedBy,CreatedOn)上的审计字段,但我不想传递它们以在我的MVC网站上编辑页面。因此,在编辑新记录时,我必须从DB读取当前审计值,然后在将其保存到DBContext之前更新传入对象。要做到这一点,我需要找到(id),抓住当前值,然后分离。我的问题/答案在这里http://stackoverflow.com/questions/8145635/what-is-the-best-way-to-maintain-an-entitys-original-properties-when-they-are-n/8154045# 8154045 – kingdango 2011-11-16 15:27:56

0

如果CREATEDATE和CreatedBy字段不是在编辑形式,更新:首先,添加一个Detach方法您DbContext实施动作对象将不具有数据库值。

如果您在编辑表单中包含这些字段,可以避免额外的调用数据库和重置,如Ed的答案所述。然后,正常的模型绑定应该选择它们,并在更新时将它们返回给您。

+1

的确他们可以,但他们也可以编辑。我确实已经显示了这些值,就像文本一样。如果我为这些值添加编辑器字段,则必须采取其他方式来努力阻止用户决定他们的问题实际上是在10天前创建的,而不是今天早些时候创建的。 – AllenG 2011-05-16 19:55:26

+1

是的,使它们隐藏的字段会使绑定工作,但它会暴露这些字段的操纵,这是不好的,如果字段需要保持不变。 – kingdango 2011-11-16 15:29:21