2012-10-14 20 views
2

概述: 我正在使用Code First和EF 5.0開發MVC ASP.Net應用程序。我有兩個表:腳本和ScriptItems。腳本可以有多個ScriptItems。 ScriptItems也是分層的。 ScriptItems可以選擇性地屬於彼此,但是這種關係只有1層深。該關係由ScriptItem.ParentId指示。EF 5.0代碼首先向現有父項添加新的子項

問題: 使用ScriptItems創建一個新的Script條目工作得很好。當我嘗試將ScriptItems添加到現有腳本時出現問題。如果我嘗試添加沒有ParentId的ScriptItem,則一切正常。只要我嘗試添加具有ParentId的ScriptItem,就會收到FK違例異常。

詳情:

腳本類:

public class Script 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public string Description { get; set; } 
    public string Type { get; set; } 
    [ForeignKey("ProcessorId")] 
    public Processor Processor { get; set; } 
    public int ProcessorId { get; set; } 
    public string Owner { get; set; } 
    public DateTime Created { get; set; } 
    public bool Public { get; set; } 

    public List<ScriptItem> Items { get; set; } 
    public List<ScriptRun> Runs { get; set; } 

    public Script() 
    { 
     Items = new List<ScriptItem>(); 
     Created = DateTime.Now; 
    } 
} 

ScriptItem類:(截斷簡潔)

public class ScriptItem 
{ 
    [Key] 
    public int Id { get; set; } 

    [ForeignKey("ParentId")] 
    public ScriptItem Parent { get; set; } 
    public int? ParentId { get; set; } 

    public Script Script { get; set; } 
    [ForeignKey("Script")] 
    public int ScriptId { get; set; } 

,增加了腳本項功能:

private void addToScript(ScriptModel model, List<int> ids) 
    { 
     Script script = scriptRepository.GetScriptWithItems(model.ScriptId); 
     List<History> historyItems = historyRespository.History.Where(h => ids.Contains(h.Id)).ToList(); 

     ScriptItem lastScriptItem = script.Items.OrderByDescending(item => item.SortIndex).FirstOrDefault(); 
     int topSortIndex = lastScriptItem == null ? 0 : lastScriptItem.SortIndex; 

     if (script != null) 
     { 
      List<ScriptItem> newItems = new List<ScriptItem>(); 
      Mapper.CreateMap<History, ScriptItem>(); 
      foreach (History h in historyItems) 
      { 
       ScriptItem scriptItem = new ScriptItem(); 
       Mapper.Map(h, scriptItem); //Populate new ScriptItem from History entry 
       scriptItem.SortIndex = ++topSortIndex; 
       scriptItem.ScriptId = model.ScriptId; 
       scriptItem.Script = script; 

       //Only add an entry if it is NOT the parent of another entry. Otherwise, EF will duplicate the Parent entries 
       if (!historyItems.Any(his => his.ParentId == h.Id)) 
        newItems.Add(scriptItem);  
      } 
      scriptRepository.AddScriptItems(newItems); 
     } 
    } 

最後的scriptRepository.AddScripItems():

public void AddScriptItems(List<ScriptItem> items) 
    { 
     items.ForEach(item => context.Entry(item).State = System.Data.EntityState.Added); 
     context.SaveChanges(); 
    } 

考慮,我添加兩個ScriptItems A和B到現有腳本的情況。 A是B的父項。當我運行SQL Server跟蹤時,我發現嘗試插入父記錄A,但ScriptId爲0,因此導致FK違例異常。沒有線索爲什麼ScriptId是0. ScriptId在ScriptItems上正確設置,我用調試器驗證了這一點。

我沒有包含插入新腳本和項目的函數,因爲它與上面的addToScript函數非常相似。它工作正常。但如果有人想看到它,我也可以添加。

有人比我更聰明有什麼想法嗎?謝謝!

回答

0

感謝Gert Arnold指引我在正確的方向。這個問題最終來自我如何通過AutoMapper構建我的ScriptItems。我無法解釋確切的問題是什麼,但這裏是我修改後的工作代碼,以防有人發現它有用。

private void addToScript(ScriptModel model, List<int> ids) 
    { 
     Script script = scriptRepository.GetScriptWithItems(model.ScriptId); 
     List<History> historyItems = historyRespository.History.Where(h => ids.Contains(h.Id)).ToList(); 

     ScriptItem lastScriptItem = script.Items.OrderByDescending(item => item.SortIndex).FirstOrDefault(); 
     int topSortIndex = lastScriptItem == null ? 0 : lastScriptItem.SortIndex; 

     if (script != null) 
     { 
      Mapper.CreateMap<History, ScriptItem>(); 
      List<ScriptItem> Parents = new List<ScriptItem>(); 
      List<History> SourceParents = historyItems 
       .Where(h => historyItems.Any(h2 => h2.ParentId == h.Id)).ToList(); 

      SourceParents.Each(h => 
      { 
       ScriptItem parent = new ScriptItem(); 
       Mapper.Map(h, parent); 
       parent.Script = script; 
       Parents.Add(parent); 
      }); 

      historyItems.Except(SourceParents).Each(h => 
      { 
       ScriptItem child = new ScriptItem(); 
       Mapper.Map(h, child); 
       child.Script = script; 
       if (child.ParentId.HasValue) 
        child.Parent = Parents.SingleOrDefault(p => p.Id == child.ParentId); 
       script.Items.Add(child); 
      }); 

      //Todo: Get sortIndex "sorted" out 

      scriptRepository.SaveScript(script); 
     } 
    } 

scriptRepository.SaveScript只是將腳本的狀態設置爲修改並調用SaveChanges()。

3

不知道是什麼原因造成的。但我認爲它可能有助於新ScriptItem在制定其所有者腳本代替添加到script.Items,如更換

scriptItem.ScriptId = model.ScriptId; 
scriptItem.Script = script; 

通過

script.Items.Add(scriptItem); 

另一個好處是,你不必改變自己的狀態手動了:當新項目添加到追蹤集合時,變更跟蹤器知道足夠多。我甚至想知道在你的劇本中這樣做是否必要,因爲設置Script也應該足夠了。

也許設置scriptScriptId改變狀態干涉太多與EF自己的邏輯,並把它偏離軌道。

+0

你先生,搖滾!雖然你的答案並不是確切的解決方案,但它引導我走向最終修復之路。可悲的是,由於我仍然是一個低調的小白,所以我不能滿足你的答案。感謝您的答覆!!! –