You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何无法删除用Identity库创建的用户间多对多关联?

问题分析:Identity多对多关联删除失败的原因及解决方法

问题场景

使用ASP.NET Identity创建用户,并通过AppUser类定义用户间的多对多关联:

public class AppUser:IdentityUser
{
    public AppUser()
    {
        MainUser = new List<AppUser>();
        UserConnection = new List<AppUser>();
    }
    public ICollection<AppUser> MainUser { get; set; } 
    public ICollection<AppUser> UserConnection { get; set; }
}

基类中获取当前用户的代码:

protected AppUser CurrentUser => userManager.FindByNameAsync(User.Identity.Name).Result;

添加关联的接口可正常工作:

[HttpPost]
 public IActionResult AddConnection(UserViewModel model)
{
  AppUser searchedUser = userManager.FindByNameAsync(model.UserName).Result;
  CurrentUser.UserConnection.Add(searchedUser);
  Context.SaveChanges();
  return Redirect($"FindConnection/{model.Search}");
}

但删除关联的接口无法生效:

[HttpPost]
public IActionResult RemoveConnection(UserViewModel model)
{
  AppUser user = Context.Users.FirstOrDefault(x => x.UserName == model.UserName);
  CurrentUser.UserConnection.Remove(user);
  Context.SaveChanges();
  return Redirect($"FindConnection/{model.Search}");
}

核心原因

1. 关联集合未被加载(延迟加载问题)

CurrentUser通过userManager.FindByNameAsync获取时,EF默认不会自动加载导航属性UserConnection(未启用延迟加载或未显式加载)。此时CurrentUser.UserConnection为空集合,Remove(user)操作找不到要删除的关联项,自然不会生成删除语句。

而添加关联时,即使集合未加载,Add(searchedUser)会直接将新关联加入集合,EF会跟踪新增操作并生成插入语句,因此可以生效。

2. 实体上下文跟踪不一致

删除方法中,user是从Context.Users直接查询的实例,而CurrentUser来自userManager的上下文。如果两个实例不属于同一个DbContext跟踪范围,EF无法识别CurrentUser.UserConnection中包含该user,导致Remove操作无效。

3. 隐式多对多的EF处理逻辑

EF Core(5.0+支持的隐式多对多)需要确保关联的两端实体都被上下文跟踪,且导航集合已加载,才能正确识别并删除中间表的关联记录。


解决方案

方案1:显式加载当前用户的关联集合

在删除方法中重新获取当前用户,并显式包含UserConnection集合,确保EF能跟踪到关联关系:

[HttpPost]
public async Task<IActionResult> RemoveConnection(UserViewModel model)
{
  // 显式加载当前用户的UserConnection集合
  var currentUser = await Context.Users
                                 .Include(u => u.UserConnection)
                                 .FirstOrDefaultAsync(u => u.UserName == User.Identity.Name);
  if(currentUser == null) return NotFound();

  var user = await Context.Users.FirstOrDefaultAsync(x => x.UserName == model.UserName);
  if(user != null)
  {
    currentUser.UserConnection.Remove(user);
    await Context.SaveChangesAsync();
  }
  return Redirect($"FindConnection/{model.Search}");
}

方案2:显式加载现有集合

如果要保留CurrentUser的定义,可在删除前手动加载UserConnection集合:

[HttpPost]
public async Task<IActionResult> RemoveConnection(UserViewModel model)
{
  // 显式加载CurrentUser的UserConnection
  await userManager.Context.Entry(CurrentUser)
                            .Collection(u => u.UserConnection)
                            .LoadAsync();

  var user = await Context.Users.FirstOrDefaultAsync(x => x.UserName == model.UserName);
  if(user != null)
  {
    CurrentUser.UserConnection.Remove(user);
    await Context.SaveChangesAsync();
  }
  return Redirect($"FindConnection/{model.Search}");
}

方案3:修复同步调用的潜在问题

原代码中使用.Result同步等待异步方法,可能导致死锁或上下文状态异常。建议统一改为async/await模式,避免同步阻塞带来的上下文跟踪问题。


内容的提问来源于stack exchange,提问作者Berk KARASU

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 23:36:17