C# 插入实体框架子项的顺序

C# 插入实体框架子项的顺序,c#,entity-framework,entity-framework-6,C#,Entity Framework,Entity Framework 6,我有一个这样的结构 public class Son { public string Name {get;set;} public int Age {get;set;} } public class Daughter { public string Name {get;set;} public int Age {get;set;} } public class Parent { public Daughter[] Daughters {get;set;}

我有一个这样的结构

public class Son {
  public string Name {get;set;} 
  public int Age {get;set;}
}

public class Daughter {
  public string Name {get;set;} 
  public int Age {get;set;}
}

public class Parent {
  public Daughter[] Daughters {get;set;}    
  public Son[] Sons {get;set;}
}
其中有一个FK父->子和父->子

当前,在父对象上执行
上下文.SaveChanges()
时,它会保存
父对象,然后保存
子对象,然后保存
子对象。我需要它先保存子对象,再保存子对象,因为我们有一个数据库触发器,它根据子对象对子对象进行验证(如果不满足要求,则会拒绝整个过程)

这一触发显然超出了EF的知识范围

我如何指定儿子在EF中依赖于女儿,以便儿子首先插入;或者是否有可以定义插入顺序的规范或属性

附言:不要过多地研究人为的例子(比如为什么我们不把它保存在一个叫做儿童的东西下)。现实世界中的例子要复杂得多,但先救儿子后救女儿的想法已经存在了

我喜欢挑战

首先声明:我不喜欢触发器,也不喜欢为使插入顺序变得重要而建立需求。我的第一项工作是用尽一切办法来取消这一要求

至少在添加实体时,我可以看到,在进行了一些修补之后,例如,一个父实体有一个或多个子实体和一个或多个子实体,插入顺序始终是基于实体名称的字母顺序。例如,对于名为“父”、“子”和“子”的实体,插入始终为父>子>子。属性、配置、插入、甚至表名的顺序对操作没有影响,但是将实体类“Son”重命名为“ASon”会导致在子类之前插入子类。我不知道这是否会继续进行编辑,但这是一个值得考虑的问题,而不是过于陈旧。(尽管这样的东西肯定需要在系统中记录好,以防有人质疑命名约定,从而在其他东西之前插入一些东西。)

也就是说,进入一个有趣的行业

使用名为ASon的子实体强制子实体在子实体之前插入子实体,可以让EF反转插入顺序:

        using (var context = new ParentDbContext())
        {
            var parent = context.Parents.Create();
            parent.Name = "Steve";

            parent.Daughters.Add(new Daughter { Name = "Elise" });
            parent.Daughters.Add(new Daughter { Name = "Susan" });
            parent.Sons.Add(new ASon { Name = "Jason" });
            parent.Sons.Add(new ASon { Name = "Duke" });
            context.Parents.Add(parent);
            context.SaveChanges();
        }
开箱即用,插入父母、儿子、儿子、女儿、女儿

为了扭转这种局面,我否决了SaveChanges,希望我们的儿子们将储蓄推迟到其他事情之后:

    public override int SaveChanges()
    {
        var trackedStates = new[] { EntityState.Added, EntityState.Modified };
        var trackedParentIds = ChangeTracker.Entries<Parent>().Where(x => trackedStates.Contains(x.State)).Select(x => x.Entity.ParentId).ToList();
        var addedSons = ChangeTracker.Entries<ASon>().Where(x => x.State == EntityState.Added).ToList();
        var modifiedSons = ChangeTracker.Entries<ASon>().Where(x => x.State == EntityState.Modified).ToList();

        int tempid = -1;
        int modifiedParentCount = addedSons.Select(x => x.Entity.Parent.ParentId)
            .Where(x => trackedParentIds.Contains(x))
            .Count();
        List<Tuple<Parent, ASon>> associatedSons = new List<Tuple<Parent, ASon>>();
        modifiedSons.ForEach(x => { x.State = EntityState.Unchanged; });
        addedSons.ForEach(x =>
        {
            x.Entity.SonId = tempid--;
            associatedSons.Add(new Tuple<Parent, ASon>(x.Entity.Parent, x.Entity));
            x.Entity.Parent.Sons.Remove(x.Entity);
            x.State = EntityState.Unchanged;
        });

        var result = base.SaveChanges();

        addedSons.ForEach(x => { x.Entity.Parent = associatedSons.Single(a => a.Item2 == x.Entity).Item1; x.State = EntityState.Added; });
        modifiedSons.ForEach(x => { x.State = EntityState.Modified; });

        result += base.SaveChanges() - modifiedParentCount;
        return result;
    }
public override int SaveChanges()
{
var trackedStates=new[]{EntityState.Added,EntityState.Modified};
var trackedParentIds=ChangeTracker.Entries()。其中(x=>trackedStates.Contains(x.State))。选择(x=>x.Entity.ParentId.ToList();
var addedSons=ChangeTracker.Entries()。其中(x=>x.State==EntityState.Added.ToList();
var modifiedSons=ChangeTracker.Entries();
int tempid=-1;
int modifiedParentCount=adddsons.Select(x=>x.Entity.Parent.ParentId)
.Where(x=>trackedParentIds.Contains(x))
.Count();
List associatedSons=新列表();
modifiedSons.ForEach(x=>{x.State=EntityState.Unchanged;});
addedSons.ForEach(x=>
{
x、 Entity.SonId=tempid--;
Add(新元组(x.Entity.Parent,x.Entity));
x、 实体。父项。子项。删除(x.Entity);
x、 状态=实体状态。未更改;
});
var result=base.SaveChanges();
addedSons.ForEach(x=>{x.Entity.Parent=associatedSons.Single(a=>a.Item2==x.Entity).Item1;x.State=EntityState.Added;});
modifiedSons.ForEach(x=>{x.State=EntityState.Modified;});
结果+=base.SaveChanges()-modifiedParentCount;
返回结果;
}
那么这是在做什么: 第一点很简单,我们找到了添加和修改的子元素。我们还统计了修改和添加儿子的父母数量。完成此操作后,这些将被重复计算

对于修改后的子对象,我们只是将其状态设置为未更改。 为了增加儿子,我们需要做一些肮脏的工作。我们需要给他们一个临时的唯一ID,因为要将他们标记为未更改,EF仍然希望跟踪他们的ID,并且当您添加两个儿子时,它将在这里失败。请注意,当我们将它们放回添加的位置时,它们将从标识列接收正确的ID,而不是这些临时的负ID。我们还跟踪它们添加的子元素与它们各自的父元素在元组中的关联,因为我们需要临时将这些子元素从它们的父元素中删除。最后,添加的子也标记为未更改。 现在,我们称之为基础SaveChanges,它将拯救我们的父母和他们的女儿。 对于修改后的子系统,我们只需要将状态更新回modified。 对于添加的子对象,我们使用保存的关联将其重新分配给其父对象,然后将其状态设置回“已添加”。 我们再次调用基本SaveChanges,将受影响的行数附加到第一次运行,并减去重复的父引用。(由于被修改而已计数的父项)

粗略的一点是调整双重保存的结果计数,这可能不是100%准确,但只有在使用SaveChanges的结果时才应该是一个问题。我不能说我曾经真正关注过这个返回值:)


希望这能给你一些想法。

也许可以给你的“儿子”类添加
[必需]
属性的
公共女儿所有者Daughter