Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/entity-framework/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 实体框架在批量添加具有多个外键的对象时的奇怪行为_C#_Entity Framework_Entity Framework Core - Fatal编程技术网

C# 实体框架在批量添加具有多个外键的对象时的奇怪行为

C# 实体框架在批量添加具有多个外键的对象时的奇怪行为,c#,entity-framework,entity-framework-core,C#,Entity Framework,Entity Framework Core,我一次创建了一个实体集合。我实际上是在创建一个双链接列表数据结构,其中每个对象都有一个下一个和上一个ID。我将所有对象添加到DB上下文中,然后遍历实体,设置它们的下一个和上一个ID。最后,我调用SaveChangesSync将更改提交到数据库。如果我单独更新实体,我可以很好地实现这一点,但显然这会导致性能问题,而且批量添加效率更高。在正确的状态下,列表中的第一个实体的前一个ID为空,列表中的最后一个实体的下一个ID为空 我的批量添加的奇怪之处在于,前面的ID是空的。基于我下面的简单(非重构)代码

我一次创建了一个实体集合。我实际上是在创建一个双链接列表数据结构,其中每个对象都有一个下一个和上一个ID。我将所有对象添加到DB上下文中,然后遍历实体,设置它们的下一个和上一个ID。最后,我调用SaveChangesSync将更改提交到数据库。如果我单独更新实体,我可以很好地实现这一点,但显然这会导致性能问题,而且批量添加效率更高。在正确的状态下,列表中的第一个实体的前一个ID为空,列表中的最后一个实体的下一个ID为空

我的批量添加的奇怪之处在于,前面的ID是空的。基于我下面的简单(非重构)代码,我对上一个ID和下一个ID做了完全相同的事情。由于某些原因,Entity Framework仅为下一个ID生成正确的ID。这两个属性在DB架构中的配置方式完全相同

for(var i=0; i < added.Count; i++)
{
    if(i == 0)
    {
        added[i].Entity.Next = added[i + 1].Entity;
    }
    if(i > 0 && i < (added.Count - 1)) {
        added[i].Entity.Previous = added[i - 1].Entity;
        added[i].Entity.Next = added[i + 1].Entity;
    }
    if(i == (added.Count - 1))
    {
        added[i].Entity.Previous = added[i - 1].Entity;
    }
}
await _dbContext.SaveChangesAsync();
for(变量i=0;i0&&i<(added.Count-1)){
添加了[i].Entity.Previous=添加了[i-1].Entity;
添加了[i].Entity.Next=添加了[i+1].Entity;
}
如果(i==(added.Count-1))
{
添加了[i].Entity.Previous=添加了[i-1].Entity;
}
}
wait_dbContext.saveChangesSync();
结果:

修改逻辑以专门更新ID,如下所示:

for(var i=0; i < added.Count; i++)
{
    if(i == 0)
    {
        added[i].Entity.NextId = added[i + 1].Entity.Id;
    }
    if(i > 0 && i < (added.Count - 1)) {
        added[i].Entity.PreviousId = added[i - 1].Entity.Id;
        added[i].Entity.NextId = added[i + 1].Entity.Id;
    }
    if(i == (added.Count - 1))
    {
        added[i].Entity.PreviousId = added[i - 1].Entity.Id;
    }
}

await _dbContext.SaveChangesAsync();
for(变量i=0;i0&&i<(added.Count-1)){
添加了[i].Entity.PreviousId=添加了[i-1].Entity.Id;
添加了[i].Entity.NextId=添加了[i+1].Entity.Id;
}
如果(i==(added.Count-1))
{
添加了[i].Entity.PreviousId=添加了[i-1].Entity.Id;
}
}
wait_dbContext.saveChangesSync();
结果:

奇怪的是,在第一个示例中(我认为这是链接EF中实体的正确方法),在SaveChanges之后,调试器显示前一个ID为null,但其导航属性包含具有正确生成ID的正确实体(不是大的负值)


这是参考中的循环,显然不是自动解决的

如果你有两项,a,b 你把a链接到b,b链接到a,你想先保存哪一个

ID是在保存时生成的,因此如果先保存“a”,则“a”的ID是已知的, 然后,您可以保存“b”,并带有指向“a”的链接,并且您知道“a”的id。 但是,当您保存“a”时,“b”的id还不知道

所以这只能在一个方向上起作用

使用自引用表时,如果您知道这种行为,则始终应首先保存项目,然后在第二次运行时设置链接。当您自己编写SQL时,以不同的方式也不可能做到这一点。ID在insert时生成,并在insert完成后已知。 因此,在插入之后,当您需要一些更新操作时,这不是EF的错

在实体框架中,可能会有这样的场景的自动复制,但你们证明了,并没有

这种类型的表也会导致导航属性出现问题,如果不按需加载(使其成为虚拟的),或者使用Include(x=>x.Next),则可能总是加载整个表,因为整个表是一个长长的项目链

对于您的场景,为每个项目分配一个连续的数字会更好。如果您使用此号码“orderby”,则表示您已保留订单。如果要在现有元素之间插入元素,请将此数字设为双精度(浮点),然后始终可以在元素7和8之间插入元素7.5。这比更新3行要容易得多,只需插入一个元素,就可以完成链接列表的操作

使用这种方法读取直接的前置或后续ID稍微慢一点,您不需要寻找具体的ID,而是寻找比您给定的想法更低的最大ID。
但是,如果在这些引用列上放置索引,服务器将在合理的时间内执行此操作。计算一个范围的最大值需要访问每个元素,而搜索可以跳过几个元素,但除非您有千兆字节的数据,否则这不值得考虑。

此实体是如何配置的?特别是PK配置和这两列的配置将非常有用。