EF 5.0 Code First首先将新子项添加到现有父项

时间:2012-10-14 16:50:05

标签: c# entity-framework ef-code-first

概述: 我正在使用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函数非常相似。它工作正常。但如果有人想看到它,我也可以加上它。

比我聪明的人有什么想法?谢谢!

2 个答案:

答案 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()。