概述: 我正在使用Code First和EF 5.0开发MVC ASP.Net应用程序。我有两个表:Scripts和ScriptItems。脚本可以有多个ScriptItems。 ScriptItems也是分层的。 ScriptItems可以选择性地彼此相关,但是这种关系只有1级深感谢。这种关系由ScriptItem.ParentId指示。
问题: 使用ScriptItems创建一个新的Script条目可以正常工作。当我尝试将ScriptItems添加到现有脚本时出现问题。如果我尝试添加没有ParentId的ScriptItems,一切正常。一旦我尝试添加具有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上设置正确,我用调试器验证了这一点。
我没有包含插入新Scripts和Items的函数,因为它与上面的addToScript函数非常相似。它工作正常。但如果有人想看到它,我也可以加上它。
比我聪明的人有什么想法?谢谢!
答案 0 :(得分:3)
不确定是什么原因引起的。但我认为将ScriptItem
添加到script.Items
而不是设置其所有者脚本可能有所帮助,即替换
scriptItem.ScriptId = model.ScriptId;
scriptItem.Script = script;
通过
script.Items.Add(scriptItem);
另一个优点是您不必再手动更改其状态:更改跟踪器足够了解何时将新项目添加到跟踪的集合中。我甚至怀疑你的脚本中是否有必要这样做,因为设置Script
也应该足够了。
也许设置script
和 ScriptId
和更改状态会干扰EF自身的逻辑并使其偏离轨道。
答案 1 :(得分: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只是将脚本的状态设置为modified并调用SaveChanges()。