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

如何使用EF Core LINQ将多对多查询转换为含角色名的UserDTO?

问题描述

我有两个由EF Core Scaffold自动生成的实体类:

public partial class Role : IEntityWithDTO<ClientRoleDTO>
{
    public Guid Id { get; set; }

    public string Name { get; set; } = null!;

    public virtual ICollection<User> Users { get; set; } = new List<User>();
}

public partial class User : IEntityWithDTO<UserDTO>
{
    public Guid Id { get; set; }

    public string Login { get; set; } = null!;
    public string Pass { get; set; } = null!;

    public virtual ICollection<Role> Roles { get; set; } = new List<Role>();
}

对应的UserDTO类定义如下:

public class UserDTO: IDTO
{
    public required Guid Id { get; set; }
    public required string Login { get; set; }
    public List<string>? Roles { get; set; }
}

我需要编写查询语句,返回包含角色名称列表的UserDTO。目前已实现查询包含Role对象列表的User实体,代码如下:

[HttpGet]
[Route("/user")]
public async Task<IActionResult> GetUserAsync([FromQuery] Guid userId)
{
    using var dbContext = new DatabaseContext();
    var user = await dbContext.Users
                              .Where(user => user.Id == userId)
                              .Include(user => user.Roles)
                              .FirstOrDefaultAsync();
        
    if (group is not null)
        return Ok(user);
    else
        return NotFound();
}

请问如何修改代码以实现需求?

解决方案

有两种常见方式可以实现需求,推荐第一种(直接投影),因为EF Core会自动优化SQL查询,只获取需要的字段,性能更优。

方式一:查询时直接投影为UserDTO

不需要先查询完整的User实体,直接在LINQ查询中构造UserDTO:

[HttpGet]
[Route("/user")]
public async Task<IActionResult> GetUserAsync([FromQuery] Guid userId)
{
    using var dbContext = new DatabaseContext();
    var userDto = await dbContext.Users
                                .Where(u => u.Id == userId)
                                .Select(u => new UserDTO
                                {
                                    Id = u.Id,
                                    Login = u.Login,
                                    Roles = u.Roles.Select(r => r.Name).ToList()
                                })
                                .FirstOrDefaultAsync();
        
    if (userDto is not null)
        return Ok(userDto);
    else
        return NotFound();
}

说明:这种方式下EF Core会生成只查询Users.Id、Users.Login和Roles.Name的SQL,避免加载不必要的字段(比如Pass)和完整的Role实体,性能更好。

方式二:先查询实体再手动映射为UserDTO

如果需要保留原有的实体查询逻辑,可以在获取到User实体后手动转换为UserDTO:

[HttpGet]
[Route("/user")]
public async Task<IActionResult> GetUserAsync([FromQuery] Guid userId)
{
    using var dbContext = new DatabaseContext();
    var user = await dbContext.Users
                              .Where(u => u.Id == userId)
                              .Include(u => u.Roles)
                              .FirstOrDefaultAsync();
        
    if (user is not null)
    {
        var userDto = new UserDTO
        {
            Id = user.Id,
            Login = user.Login,
            Roles = user.Roles.Select(r => r.Name).ToList()
        };
        return Ok(userDto);
    }
    else
    {
        return NotFound();
    }
}

说明:这种方式会加载完整的User实体和关联的Role实体,包括不需要的Pass字段,性能略逊于第一种方式。

另外注意原代码中的if (group is not null)是笔误,两种方案中已修正为if (user is not null)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 15:56:28