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