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

Entity Framework Core 如何查询用户含组层级继承的所有角色

EF Core 查询包含多级组继承的用户所有角色实现方案

前置优化提醒

你的User实体中GroupId字段需要调整为可空类型,否则顶级用户组没有上级节点时无法正常存储:

public Guid? GroupId { get; set; }

方案1:迭代遍历(适合层级较浅的场景)

写法简单易理解,不需要写原生SQL,适合组层级最多不超过5层的场景:

// 输入参数:待查询用户Id userId
// 1. 获取目标用户基础信息
var targetUser = await context.Users.FirstAsync(u => u.Id == userId);
// 2. 收集所有需要查询角色的主体:用户本身 + 所有上级组
var subjectList = new List<(Guid SubjectId, string SourceName)>
{
    (targetUser.Id, "直接分配") // 用户直接绑定的角色来源标记为直接分配
};

// 迭代向上遍历所有上级组
var currentGroupId = targetUser.GroupId;
while (currentGroupId.HasValue)
{
    var group = await context.Users.FirstOrDefaultAsync(u => u.Id == currentGroupId.Value && u.Type == "Group");
    if (group == null) break;
    subjectList.Add((group.Id, group.Name));
    currentGroupId = group.GroupId;
}

// 3. 批量查询所有主体绑定的角色,返回要求的字段
var roleResult = await context.UserRoles
    .Where(ur => subjectList.Select(s => s.SubjectId).Contains(ur.UserId))
    .Select(ur => new 
    {
        RoleName = ur.Role.Name,
        InheritFrom = subjectList.First(s => s.SubjectId == ur.UserId).SourceName
    })
    .ToListAsync();

方案2:递归CTE查询(适合层级较深的高性能场景)

EF Core 8及以上版本支持原生SQL结合LINQ查询,直接在数据库端完成递归层级遍历,只需要一次数据库请求,性能更高:

// 输入参数:待查询用户Id userId
var roleResult = await context.UserRoles
    .FromSql($"""
        WITH RecursiveSubjects AS
        (
            -- 初始节点:目标用户
            SELECT Id, GroupId
            FROM Users
            WHERE Id = {userId}
            UNION ALL
            -- 递归节点:向上匹配所有上级组
            SELECT u.Id, u.GroupId
            FROM Users u
            INNER JOIN RecursiveSubjects rs ON u.Id = rs.GroupId
            WHERE u.Type = 'Group'
        )
        SELECT ur.*
        FROM UserRoles ur
        INNER JOIN RecursiveSubjects rs ON ur.UserId = rs.Id
    """)
    .Select(ur => new 
    {
        RoleName = ur.Role.Name,
        InheritFrom = ur.UserId == userId ? "直接分配" : ur.User.Name
    })
    .ToListAsync();

注:EF Core会自动对插值的{userId}做参数化处理,不存在SQL注入风险。


结果处理说明

  • 如果同一个角色同时来自多个来源(比如用户直接分配了该角色,同时所属组也分配了同一个角色),上述查询会返回多条记录,若需要去重可在查询最后添加 .DistinctBy(x => x.RoleName) 仅保留唯一角色
  • 如果你不需要保留所有来源,仅需要角色列表,可调整返回的匿名类结构,只保留RoleName字段即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.09 21:21:02