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

C#基于EF Core 3.1实现好友的好友中同城市非好友路径查询

实现方案

前置说明

  • 好友关系属于无向图,查询单个用户的直接好友时需要同时覆盖Member1和Member2两种关联场景
  • 路径搜索采用广度优先搜索(BFS),天然保证优先返回最短路径,可根据业务需要限制最大搜索深度(比如最多3度好友,避免性能爆炸)
  • 优先过滤同城市用户再做路径匹配,可大幅降低搜索范围

步骤1:封装获取用户直接好友列表的通用方法

首先注入ApplicationDbContext后,先写工具方法获取指定用户的所有直接好友ID,方便后续调用:

// 获取指定用户的所有直接好友ID集合
private HashSet<int> GetDirectFriendIds(int memberId)
{
    return _context.Friends
        .Where(f => f.Member1Id == memberId)
        .Select(f => f.Member2Id)
        .Concat(_context.Friends
            .Where(f => f.Member2Id == memberId)
            .Select(f => f.Member1Id))
        .ToHashSet();
}

步骤2:实现BFS路径搜索核心逻辑

以下是完整的业务实现方法,入参为指定用户ID、最大搜索深度,返回符合要求的路径集合:

public List<string> SearchSameCityNonFriendWithPath(int targetMemberId, int maxDepth = 3)
{
    // 读取目标用户基础信息,为空直接返回空结果
    var targetMember = _context.Members.Find(targetMemberId);
    if (targetMember == null) return new List<string>();
    
    var directFriendIds = GetDirectFriendIds(targetMemberId);
    var result = new List<string>();
    // 记录已访问用户ID,避免循环遍历
    var visited = new HashSet<int> { targetMemberId };
    // BFS队列,每个元素存储当前用户ID、当前路径列表
    var queue = new Queue<(int MemberId, List<string> Path)>();
    
    // 队列初始化:把目标用户的直接好友先入队
    foreach (var friendId in directFriendIds)
    {
        var friend = _context.Members.Find(friendId);
        if (friend == null) continue;
        visited.Add(friendId);
        queue.Enqueue((friendId, new List<string> { targetMember.Name, friend.Name }));
    }

    // 开始BFS遍历
    while (queue.Count > 0)
    {
        var current = queue.Dequeue();
        var currentDepth = current.Path.Count - 1;
        
        // 超过最大搜索深度直接跳过
        if (currentDepth >= maxDepth) continue;
        
        // 读取当前节点的所有好友
        var currentFriendIds = GetDirectFriendIds(current.MemberId);
        foreach (var friendId in currentFriendIds)
        {
            // 已访问过的节点跳过,避免环路
            if (visited.Contains(friendId)) continue;
            visited.Add(friendId);
            
            var currentFriend = _context.Members.Find(friendId);
            if (currentFriend == null) continue;
            
            // 拼接新的路径
            var newPath = new List<string>(current.Path) { currentFriend.Name };
            
            // 校验是否符合要求:同城市、不是目标用户的直接好友、不是目标用户本人
            if (currentFriend.City == targetMember.City 
                && !directFriendIds.Contains(friendId) 
                && friendId != targetMemberId)
            {
                // 按要求格式化路径字符串
                result.Add($"{string.Join(" -- ", newPath)}(\"{targetMember.City}\")");
            }
            
            // 新节点入队,继续下一层搜索
            queue.Enqueue((friendId, newPath));
        }
    }

    return result;
}

性能优化建议

  • 如果数据量较大,建议提前把目标城市所有用户的好友关系一次性加载到内存,避免循环查询数据库:
// 提前加载目标城市所有用户的好友关系
var targetCity = targetMember.City;
var allCityMembers = _context.Members
    .Where(m => m.City == targetCity)
    .Include(m => m.MemberFriends)
    .Include(m => m.MemberFriendsOf)
    .ToList();
// 后续所有好友查询直接从该内存集合读取,减少数据库交互次数
  • 最大搜索深度建议不要超过4,否则性能会呈指数级下降
  • 如果需要返回所有可能路径而非仅最短路径,去掉visited集合的判断逻辑,改为记录访问路径避免环路即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 05:15:10