2011-05-16 71 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