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

ASP.NET MVC用户互关系统开发咨询(无多对多表开发经验)

ASP.NET MVC 用户互相关注系统开发指南

一、修正实体模型

你当前的Follow实体导航属性定义有误,需要明确区分关注者和被关注者的关联关系,同时给Author实体添加对应的集合导航属性:

1. 调整Follow实体

namespace EntityLayer.Concrete
{
    public partial class Follow
    {
        [Key]
        public int FollowID { get; set; }
        
        // 关注者ID(发起关注的用户)
        public int FollowerID { get; set; }

        // 被关注者ID(被关注的用户)
        public int FollowingID { get; set; }
        
        // 标记当前是否处于关注状态(用于软删除/取消关注)
        public bool IsFollowing { get; set; }

        // 导航属性:关联关注者
        public virtual Author Follower { get; set; }
        // 导航属性:关联被关注者
        public virtual Author Following { get; set; }
    }
}

2. 补充Author实体导航属性

namespace EntityLayer.Concrete
{
    public class Author
    {
        [Key]
        public int AuthorID { get; set; }
        // 其他用户属性,根据需求添加
        public string Username { get; set; }
        public string Email { get; set; }

        // 导航属性:当前用户关注的所有人
        public virtual ICollection<Follow> FollowingList { get; set; }
        // 导航属性:关注当前用户的所有人(粉丝)
        public virtual ICollection<Follow> FollowerList { get; set; }

        // 初始化集合避免空引用
        public Author()
        {
            FollowingList = new HashSet<Follow>();
            FollowerList = new HashSet<Follow>();
        }
    }
}

二、配置EF实体关系(Fluent API)

在你的DbContext类中,通过Fluent API明确多对多关联的外键规则,避免级联删除冲突:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // 配置Follow与关注者的关联
    modelBuilder.Entity<Follow>()
        .HasRequired(f => f.Follower)
        .WithMany(a => a.FollowingList)
        .HasForeignKey(f => f.FollowerID)
        .WillCascadeOnDelete(false);

    // 配置Follow与被关注者的关联
    modelBuilder.Entity<Follow>()
        .HasRequired(f => f.Following)
        .WithMany(a => a.FollowerList)
        .HasForeignKey(f => f.FollowingID)
        .WillCascadeOnDelete(false);
}

三、实现数据访问层(DAL)

创建FollowRepository封装基础数据操作:

public class FollowRepository : IDisposable
{
    private YourDbContext _context = new YourDbContext();

    // 添加或更新关注状态(已存在则切换IsFollowing,不存在则新增)
    public void AddOrUpdateFollow(int followerId, int followingId, bool isFollowing)
    {
        var existingFollow = _context.Follows.FirstOrDefault(f => 
            f.FollowerID == followerId && f.FollowingID == followingId);
        
        if (existingFollow != null)
        {
            existingFollow.IsFollowing = isFollowing;
        }
        else
        {
            _context.Follows.Add(new Follow
            {
                FollowerID = followerId,
                FollowingID = followingId,
                IsFollowing = isFollowing
            });
        }
        _context.SaveChanges();
    }

    // 获取用户的关注列表
    public List<Author> GetFollowingList(int userId)
    {
        return _context.Follows
            .Where(f => f.FollowerID == userId && f.IsFollowing)
            .Select(f => f.Following)
            .ToList();
    }

    // 获取用户的粉丝列表
    public List<Author> GetFollowerList(int userId)
    {
        return _context.Follows
            .Where(f => f.FollowingID == userId && f.IsFollowing)
            .Select(f => f.Follower)
            .ToList();
    }

    // 检查是否已关注目标用户
    public bool IsFollowingUser(int followerId, int followingId)
    {
        return _context.Follows.Any(f => 
            f.FollowerID == followerId && f.FollowingID == followingId && f.IsFollowing);
    }

    public void Dispose()
    {
        _context.Dispose();
    }
}

四、实现业务逻辑层(BLL)

封装业务规则,比如禁止关注自己:

public class FollowManager
{
    // 切换关注/取消关注状态
    public void ToggleFollow(int currentUserId, int targetUserId)
    {
        if (currentUserId == targetUserId)
            throw new InvalidOperationException("无法关注自己");

        using (var repo = new FollowRepository())
        {
            bool isAlreadyFollowing = repo.IsFollowingUser(currentUserId, targetUserId);
            repo.AddOrUpdateFollow(currentUserId, targetUserId, !isAlreadyFollowing);
        }
    }

    // 获取指定用户的关注列表
    public List<Author> GetUserFollowing(int userId)
    {
        using (var repo = new FollowRepository())
        {
            return repo.GetFollowingList(userId);
        }
    }

    // 获取指定用户的粉丝列表
    public List<Author> GetUserFollowers(int userId)
    {
        using (var repo = new FollowRepository())
        {
            return repo.GetFollowerList(userId);
        }
    }
}

五、MVC控制器与视图实现

1. 控制器逻辑

public class UserController : Controller
{
    private readonly FollowManager _followManager = new FollowManager();

    // 异步处理关注/取消关注请求
    [HttpPost]
    [Authorize]
    public ActionResult ToggleFollow(int targetUserId)
    {
        // 这里根据你的用户认证方式获取当前登录用户ID,示例用ASP.NET Identity
        int currentUserId = Convert.ToInt32(User.Identity.GetUserId());
        
        try
        {
            _followManager.ToggleFollow(currentUserId, targetUserId);
            return Json(new { success = true });
        }
        catch (Exception ex)
        {
            return Json(new { success = false, message = ex.Message });
        }
    }

    // 展示用户的关注列表
    public ActionResult Following(int userId)
    {
        var followingList = _followManager.GetUserFollowing(userId);
        return View(followingList);
    }

    // 展示用户的粉丝列表
    public ActionResult Followers(int userId)
    {
        var followerList = _followManager.GetUserFollowers(userId);
        return View(followerList);
    }
}

2. 视图示例(用户详情页的关注按钮)

@model EntityLayer.Concrete.Author

@if (User.Identity.IsAuthenticated)
{
    int currentUserId = Convert.ToInt32(User.Identity.GetUserId());
    var followManager = new FollowManager();
    bool isFollowing = followManager.IsFollowingUser(currentUserId, Model.AuthorID);
    
    <button id="followBtn" data-target-id="@Model.AuthorID" 
            class="btn @(isFollowing ? "btn-secondary" : "btn-primary")">
        @(isFollowing ? "取消关注" : "关注")
    </button>
}

<script>
    $("#followBtn").click(function () {
        const targetId = $(this).data("target-id");
        $.post("/User/ToggleFollow", { targetUserId: targetId }, function (result) {
            if (result.success) {
                const btn = $("#followBtn");
                if (btn.text() === "关注") {
                    btn.text("取消关注").removeClass("btn-primary").addClass("btn-secondary");
                } else {
                    btn.text("关注").removeClass("btn-secondary").addClass("btn-primary");
                }
            } else {
                alert(result.message);
            }
        });
    });
</script>

六、关键注意事项

  • 用户认证:确保使用ASP.NET Identity或自定义认证系统正确获取当前用户ID
  • 性能优化:查询关注/粉丝列表时,可使用Include方法预加载关联数据,避免N+1查询问题
  • 权限控制:可扩展逻辑支持用户设置隐私,禁止未授权用户关注
  • 软删除逻辑:通过IsFollowing字段标记状态,比物理删除更灵活,支持恢复关注

内容的提问来源于stack exchange,提问作者Sıla Yıldırım

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 18:11:01