EF Core .NET6植入种子数据保存时报UserRole.UserId值未知错误
这个报错是.NET 6配套EF Core 6的校验规则变更导致的。.NET 5使用的EF Core版本对关联实体的外键依赖校验更宽松,升级到EF Core 6后,保存变更时会严格检查所有被追踪实体的外键依赖状态:
你从JSON文件反序列化User列表时,如果JSON结构里包含UserRoles字段,会同步生成对应的UserRole对象集合挂在User的导航属性上。这些自动生成的UserRole对象既没有赋值UserId外键(此时User还没入库,主键ID还未生成),也没有和待创建的User建立正确的追踪关联,属于游离实体。调用_userManager.CreateAsync触发保存时,EF Core检测到这些外键值未知的UserRole实体,就会抛出你看到的异常。
二选一即可,优先选方法1,改动最小最稳妥。
方法1:反序列化后清空关联导航属性
反序列化完用户数据后,手动把User上挂的游离UserRoles集合清空,避免这些无效关联实体被上下文追踪:var users = JsonSerializer.Deserialize<List<User>>(userData); if (users == null) return; foreach (var user in users) { // 清空反序列化带出来的游离角色关联数据 user.UserRoles.Clear(); user.UserName = user.UserName.ToLower(); user.EmailConfirmed = true; user.PhoneNumberConfirmed = true; user.IsActive = true; await _userManager.CreateAsync(user, Users.DefaultPassword); await _userManager.AddToRoleAsync(user, Roles.BasicUser); }如果后续反序列化还会带出其他导航属性数据,也可以直接在反序列化配置里全局忽略导航属性,避免同类问题。
方法2:保存前分离上下文中的无效追踪实体
如果不想修改反序列化逻辑,可以在每次调用CreateAsync前,把ChangeTracker里追踪到的无效UserRole实体设为分离状态,不参与本次保存:foreach (var user in users) { user.UserName = user.UserName.ToLower(); user.EmailConfirmed = true; user.PhoneNumberConfirmed = true; user.IsActive = true; // 分离所有未绑定有效外键的UserRole追踪条目 foreach (var entry in _context.ChangeTracker.Entries<UserRole>()) { entry.State = EntityState.Detached; } await _userManager.CreateAsync(user, Users.DefaultPassword); await _userManager.AddToRoleAsync(user, Roles.BasicUser); }
你现在写的Task.Run(async () => {...}).GetAwaiter().GetResult()属于同步阻塞异步代码的写法,在线程池资源紧张时很容易引发死锁,建议把种子数据初始化逻辑改成全异步实现,直接用await调用,不要用这种阻塞封装。
内容的提问来源于stack exchange,提问作者Sras

