如何在Entity Framework Core中更直接地为IdentityUser间的好友关系建模
问题根源
- 自引用多对多关系配置错误:你在
HasMany和WithMany中都指向了同一个Friends集合,EF Core无法区分关系的两端方向,导致生成中间表实体时无法正确赋值主键字段。 - 异步方法未加
await:你调用GetUserAsync和FindByIdAsync时没有等待异步返回,拿到的是Task<BlogUser>对象而非实际的用户实体,EF Core读取不到ID值,所以抛出User1Id为空的异常。 - 导航属性未手动加载:EF Core默认不会自动加载关联导航属性,你直接通过
UserManager获取的用户对象,Friends集合不会自动填充,所以查询返回空数组。 - 调换配置后返回自己:是因为关系方向配置颠倒,EF Core把中间表的
User1Id对应的用户当作了好友,自然查询出当前用户自己。
解决方案
步骤1:调整实体定义
为自引用多对多区分双向导航,不要复用同一个Friends集合:
public class BlogUser : IdentityUser { public virtual ICollection<Blog> Blogs { get; set; } // 我关注的好友列表 public virtual ICollection<BlogUser> Friends { get; set; } // 关注我的粉丝列表 public virtual ICollection<BlogUser> Followers { get; set; } public virtual ICollection<BlogUserFriendship> Friendships { get; set; } }
BlogUserFriendship实体不需要改动。
步骤2:修正关系配置
protected override void OnModelCreating(ModelBuilder builder) { // 必须调用基类方法,保证Identity相关配置生效 base.OnModelCreating(builder); builder.Entity<BlogUser>() // 左集合:当前用户的好友 .HasMany(u => u.Friends) // 右集合:当前用户的粉丝 .WithMany(u => u.Followers) .UsingEntity<BlogUserFriendship>( j => j .HasOne(fs => fs.User2) .WithMany() .HasForeignKey(fs => fs.User2Id) .OnDelete(DeleteBehavior.Restrict), j => j .HasOne(fs => fs.User1) .WithMany(u => u.Friendships) .HasForeignKey(fs => fs.User1Id), j => { j.HasKey(fs => new { fs.User1Id, fs.User2Id }); } ); }
步骤3:修正添加好友的代码
注意补全await,不需要用UserManager.UpdateAsync,直接通过DbContext保存即可:
// 等待异步方法返回实际用户实体 var user = await _userManager.GetUserAsync(this.User); var friendToAdd = await _userManager.FindByIdAsync(model.UserId); // 附加当前用户到上下文,加载Friends集合 _context.Users.Attach(user); user.Friends ??= new List<BlogUser>(); user.Friends.Add(friendToAdd); // 如果是双向好友(加好友需要对方同意的场景不用加这行) // friendToAdd.Friends.Add(user); await _context.SaveChangesAsync();
步骤4:修正查询好友的代码
手动Include加载Friends导航属性:
[Route("Friends")] [HttpGet] public async Task<IActionResult> GetFriends() { var userId = _userManager.GetUserId(this.User); var user = await _context.Users .Include(u => u.Friends) .FirstOrDefaultAsync(u => u.Id == userId); return Ok(user.Friends.Select(f => new {f.UserName}).ToList()); }
可选优化
如果你的场景是双向好友关系(即A是B的好友,B也自动是A的好友),可以在业务层封装好友添加逻辑,插入中间表时同时插入(A,B)和(B,A)两条记录,或者查询时合并Friends和Followers集合返回即可。
内容的提问来源于stack exchange,提问作者DHao2001
相关产品推荐
相关产品推荐

