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

多表关联结果的LINQ分组查询:实现用户-角色-权限层级结构

正确实现用户-角色-权限嵌套分组的LINQ写法

场景1:处理已查询出的扁平结果(内存中分组)

假设你已经通过SQL或EF得到了扁平的IEnumerable<UserRolePermissionFlat>集合(包含用户、角色、权限的所有字段),可以通过双层嵌套分组实现需求:

// 最终分组结果的DTO(可根据你的实体类调整)
public class RoleWithPermissions
{
    public int RoleId { get; set; }
    public string RoleName { get; set; }
    public List<Permission> Permissions { get; set; }
}

public class UserWithRoles
{
    public int UserId { get; set; }
    public string Username { get; set; }
    public List<RoleWithPermissions> Roles { get; set; }
}

// LINQ分组逻辑
var groupedData = flatResults
    // 第一层:按用户唯一标识分组(确保同一个用户只出现一次)
    .GroupBy(urp => new { urp.UserId, urp.Username })
    .Select(userGroup => new UserWithRoles
    {
        UserId = userGroup.Key.UserId,
        Username = userGroup.Key.Username,
        // 第二层:在用户组内按角色唯一标识分组
        Roles = userGroup
            .GroupBy(urp => new { urp.RoleId, urp.RoleName })
            .Select(roleGroup => new RoleWithPermissions
            {
                RoleId = roleGroup.Key.RoleId,
                RoleName = roleGroup.Key.RoleName,
                // 提取当前角色下的所有权限
                Permissions = roleGroup
                    .Select(urp => new Permission
                    {
                        PermissionId = urp.PermissionId,
                        PermissionName = urp.PermissionName
                    })
                    .ToList()
            })
            .ToList()
    })
    .ToList();

场景2:直接通过EF Core从数据库查询(推荐)

如果使用EF Core,可直接通过关联查询+分组完成,避免先拉取全量扁平数据:

方式1:利用导航属性(已配置实体关系时)

假设你的实体类已配置多对多导航关系(User→UserRolePermission→Role,Role→UserRolePermission→Permission):

var result = _context.Users
    .Include(u => u.UserRolePermissions)
        .ThenInclude(urp => urp.Role)
            .ThenInclude(r => r.UserRolePermissions)
                .ThenInclude(urp => urp.Permission)
    .Select(u => new UserWithRoles
    {
        UserId = u.UserId,
        Username = u.Username,
        Roles = u.UserRolePermissions
            .Select(urp => urp.Role)
            .DistinctBy(r => r.RoleId) // 去重同一用户下的重复角色
            .Select(r => new RoleWithPermissions
            {
                RoleId = r.RoleId,
                RoleName = r.RoleName,
                Permissions = r.UserRolePermissions
                    .Select(urp => urp.Permission)
                    .DistinctBy(p => p.PermissionId) // 去重同一角色下的重复权限
                    .ToList()
            })
            .ToList()
    })
    .ToList();

方式2:手动Join查询(无导航属性时)

如果未配置导航属性,可手动关联四张表后分组:

var result = _context.Users
    .Join(_context.UserRolePermissions, u => u.UserId, urp => urp.UserId, (u, urp) => new { u, urp })
    .Join(_context.Roles, temp => temp.urp.RoleId, r => r.RoleId, (temp, r) => new { temp.u, r, temp.urp })
    .Join(_context.Permissions, temp => temp.urp.PermissionId, p => p.PermissionId, (temp, p) => new { temp.u, temp.r, p })
    // 按用户分组
    .GroupBy(temp => temp.u)
    .Select(userGroup => new UserWithRoles
    {
        UserId = userGroup.Key.UserId,
        Username = userGroup.Key.Username,
        // 按角色分组
        Roles = userGroup
            .GroupBy(temp => temp.r)
            .Select(roleGroup => new RoleWithPermissions
            {
                RoleId = roleGroup.Key.RoleId,
                RoleName = roleGroup.Key.RoleName,
                Permissions = roleGroup.Select(temp => temp.p).DistinctBy(p => p.PermissionId).ToList()
            })
            .ToList()
    })
    .ToList();

你之前写法失效的常见原因

  • 仅做了单层分组,没有嵌套第二层按角色分组,导致每个用户-角色-权限组合都成为独立元素
  • 分组时未使用用户/角色的唯一标识(比如仅按用户名分组,未加UserId),导致重复用户被多次统计
  • 未对角色/权限做去重处理,同一角色下出现重复权限

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 17:04:57