EF6将具有两个外键的子记录添加到父表

时间:2014-01-22 16:56:54

标签: entity-framework entity-framework-6

我的问题涉及一个任务表及其关系。

业务模型是每个任务都有一个任务列表,需要在它开始之前完成。

我的C#EF6 winforms应用程序有以下2个表(简化)

我有一个带主键的任务表

[TaskID] [int] IDENTITY(1,1) NOT NULL

和只有两列的前趋表

[TaskID] [int] NOT NULL,
[PredecessorTaskId] [int] NOT NULL

其中主键由两列组成。

TaskIDPredecessorTaskId都是任务表的外键。

在设计器edmx中,我看不到前一个表,而是从Task表中运行一条回到自身的行。

通过运行自定义工具创建的代码是

public partial class task
{
    public task()
    {
        this.NeedsTasks = new HashSet<task>();
        this.NeededByTasks = new HashSet<task>();
    }
  public int TaskID { get; set; }

  public virtual ICollection<task> NeedsTasks { get; set; }
  public virtual ICollection<task> NeededByTasks { get; set; }
  // other fields 
}

我找不到为前任表格生成的任何代码

现在我的代码

public static void SaveWorkflow(List<task> workflowTasks)
    {
        short offset = 1;
        using (var db = new MyDbContext)
        {
            task task = null;
            task prevtask = null;

            foreach (var workflowTask in workflowTasks)
            {
                workflowTask.TaskOffsetID = offset;
                offset++;
                task = db.tasks.Add(workflowTask);
                //db.SaveChanges();
                if (prevtask != null)
                {
                    task.NeedsTasks.Add(prevtask);
                }
                prevtask = task;

            }
            db.SaveChanges();
        }
    }

如果我运行上面的代码并使用

查询记录
select k.taskid, p.taskid, p.predecessortaskId 
from task k left outer join predecessor p on k.taskid = p.taskid
where  etc

我得到了

taskid  taskid  predecessortaskid
568187  568187  568188
568188  NULL    NULL
568189  568189  568187
568190  568190  568189
568191  568191  568190

这不是我想要的,因为它是应该拥有空数据的第一个前任,表明第一个任务不需要任何其他任务来启动。

如果我取消注释循环内对SaveChanges的调用,我会得到正确的结果

但是我更希望只有一次调用SaveChanges,所以有一个交易。

我也尝试添加

prevtask.NeededByTasks.Add(task);

之后

task.NeedsTasks.Add(prevtask);
然而,它没有任何区别

1 个答案:

答案 0 :(得分:1)

实际上是您的SQL结果中包含workflowTasks数据的第一个前任(即NULL列表中的第一个元素) 。如果稍微扩展SQL查询,可以看到它:

select k.taskid, p.taskid, p.predecessortaskId, k.taskoffsetid
from task k left outer join predecessor p on k.taskid = p.taskid
where  etc
order by taskoffsetid

结果现在是:

taskid  taskid  predecessortaskid  taskoffsetid
568188  NULL    NULL               1
568187  568187  568188             2
568189  568189  568187             3
568190  568190  568189             4
568191  568191  568190             5

但是,taskid的生成顺序与您设置taskoffsetid的顺序不同,并且任务在输入列表中排序。

如果插入包含许多实体的复杂对象图,则无法假定生成密钥的任何特定顺序。 EF发送到数据库的INSERT语句序列受EF控制。我不会以任何方式依赖于期望密钥是按照我想要的特定顺序生成的,除非我通过多次调用SaveChanges显式强制密钥生成(正如您已经看到的那样)。

通过将整个循环包装到SaveChanges块中,您可以解决在单个事务中运行多个using (var scope = new TransactionScope()) { ... scope.Complete(); }调用的问题。

然而,问题是:你真的需要排序taskid吗?根本没有引用密钥,您实际上可以按照依赖项的顺序运行任务,例如:

using (var db = new MyDbContext)
{
    var task = db.Tasks.Where(t => !t.NeedsTasks.Any()).SingleOrDefault();
    while (task != null)
    {
        // run task / do something with task...
        task = task.NeededByTasks.SingleOrDefault(); // lazy loading
    }
}