使用Entity Framework正确填充数据表的方法与问题排查
结论:你调整后的代码无法解决现有问题,且会触发新的异常
核心认知澄清
- Entity Framework Core的
Update()方法不存在“自动判断记录存在与否、分别执行新增/更新”的逻辑。它的实际作用是:无论传入的实体之前是否被上下文跟踪,直接将该实体及其所有关联的导航属性实体的跟踪状态强制设置为Modified。 - 如果你传入的是尚未入库的全新实体(比如Id为默认值0的Balance1),调用
Update()后EF会直接尝试对这个实体执行更新语句,匹配Id=0的数据库行,完全不会触发新增逻辑——这本质上和你之前手动把实体状态设为EntityState.Modified的效果没有区别。
你遇到的两类原始异常的根因
- 更新时关联数据被异常覆盖的问题
原有代码在命中已存在Balance1的分支时,直接给已经加载的Balance1实体的Parts集合循环添加5个全新的Balance1Part对象,既没有清理数据库中已经存在的旧关联Part记录,也没有对现有Part做增量对比。EF检测到集合中新增的未跟踪实体后,会自动把这些新Part关联到当前Balance1上提交,最终导致同一个Balance1下重复挂载多套Part数据,表现为数据被异常覆盖。 - 新增记录时主键Id始终为0的报错
你在Balance1的主键Id上标注了[DatabaseGenerated(DatabaseGeneratedOption.None)],这个配置的含义是:EF和数据库都不会自动为该主键生成值,必须由业务代码手动给Id赋值后才能正常入库。你原有代码在新建Balance1实例时从未给Id赋值,默认值就是0,提交入库时自然会触发主键重复报错。
正确的实现修正方向
- 修正主键生成策略:如果没有特殊业务要求必须手动指定Id,直接移除Balance1主键上的
[DatabaseGenerated(DatabaseGeneratedOption.None)]特性,使用默认的数据库自增主键策略,EF和数据库会在新增插入时自动生成非0的合法主键值。如果确实需要手动赋值Id,必须在新建实体时手动传入合法的唯一Id,不能保留默认的0值。 - 明确区分新增、更新分支的跟踪状态,不要笼统调用
Update():- 当查询到对应DataFileId的Balance1不存在时:实例化新Balance1、赋值DataFileId、填充数据后调用
Add()方法让上下文将其标记为Added状态,提交时会自动完成插入流程,生成合法主键。 - 当查询到对应DataFileId的Balance1已存在时:加载实体后先通过
balance1.Parts.Clear()清理上下文跟踪的旧关联Part记录,再将新生成的5个Balance1Part加入集合,由上下文自动跟踪实体的修改状态提交。
- 当查询到对应DataFileId的Balance1不存在时:实例化新Balance1、赋值DataFileId、填充数据后调用
- 移除代码中
SaveChangesAsync().Wait()的同步阻塞写法,改用await innerContext.SaveChangesAsync()异步等待提交,避免线程死锁。
内容的提问来源于stack exchange,提问作者runnerpaul
相关产品推荐
相关产品推荐

