EF Core创建新实体时为何会删除其他实体?
嘿,我太懂这种莫名其妙的崩溃了——明明只是想新增个NetworkUser,结果别的实体平白无故被删了?别急,我帮你捋捋EF Core里最容易踩的几个坑:
首先,大概率是实体关联配置+变更追踪的锅。你看你代码里获取了network和userProxy这两个实体,先想想这俩服务的查询方法(GetDetailsById和GetById)是不是用Include加载了它们的关联子实体?比如GetDetailsById(networkId)如果顺带加载了Network下的NetworkUsers集合,那麻烦就来了:
EF Core的变更追踪是盯着整个实体图的,当你把这个Network实体拿出来后,如果它的NetworkUsers集合和数据库里的实际数据不一致(比如代码里拿到的是空集合,但数据库里其实有旧的NetworkUser),EF Core就会默认认为这些旧的子实体被你从集合里移除了。要是你还配置了级联删除(比如OnDelete(DeleteBehavior.Cascade)),那保存新实体的时候,它就会顺手把那些“被移除”的旧实体删掉。
其次,可能是DbContext复用导致的状态污染。如果你的服务里用的DbContext不是每次请求都新建的,那之前操作留下的实体状态(比如某个实体被标记成了Deleted),会在这次保存的时候一起提交,导致误删。
另外,也得检查下你的实体关系配置。比如在OnModelCreating里是不是给Network和NetworkUser加了这样的配置:
modelBuilder.Entity<Network>() .HasMany(n => n.NetworkUsers) .WithOne(nu => nu.Network) .OnDelete(DeleteBehavior.Cascade);
这种级联删除配置虽然方便,但一旦变更追踪判断错了关联关系,就容易搞出删数据的大问题。
给你几个实用的解决思路:
- 别乱Include关联集合:如果你只是要把
network和userProxy关联到新的NetworkUser上,根本不需要加载它们的子实体。把GetDetailsById和GetById里的Include去掉,让EF Core只追踪这两个父实体本身,不管它们的子集合。 - 手动设置实体状态:拿到
network和userProxy后,直接把它们的状态设为Unchanged,告诉EF Core“我只是用一下它们的ID,不要跟踪它们的任何变更”:_context.Entry(network).State = EntityState.Unchanged; _context.Entry(userProxy).State = EntityState.Unchanged; - 调整级联删除策略:把不需要自动级联删除的关系改成
DeleteBehavior.Restrict或者DeleteBehavior.SetNull,就算EF Core误判,数据库也会阻止删除操作,避免数据丢失。 - 确保DbContext生命周期正确:用依赖注入的话,把DbContext的生命周期设为
Scoped,保证每次请求都是新的实例,不会带着之前的变更痕迹。
你可以先去排查下那两个查询方法里有没有Include关联集合,这绝对是最常见的触发原因!
备注:内容来源于stack exchange,提问作者Trojo

