我需要通过 Id 查询另一个查询返回的用户列表

I need to do a query on a returned list of users from another query, by Id

目前,我有一个从用户 table 获取用户信息的服务。用户可以由管理员或员工创建,所有这些员工都有自己的 ID。因此,考虑到这一点,有一个名为 CreatedBy 的列,其中包含此管理员或用户的 ID,我必须将其名称设为 return。到目前为止,我已经提取了用户模型,但现在我需要创建部分,在其中使用 CreatedBy

中的 user.Id 提取用户名

这是我从数据库中提取的 tables 用户和公司,查询参数只是名称或公司名称

public async Task<List<ApplicationUser>> SearchUsers(UserSearchDto userSearchDto)
{
    userSearchDto.FirstName ??= string.Empty;
    userSearchDto.LastName ??= string.Empty;
    userSearchDto.CompanyName ??= string.Empty;

    return await _locationDbContext.Users
    .Include(nameof(Company))
    .Where(user => user.FirstName.Contains(userSearchDto.FirstName) 
    && user.LastName.Contains(userSearchDto.LastName) 
    && user.Company.Company_Name.Contains(userSearchDto.CompanyName))
    .ToListAsync();
}

因此,在我正在 returning 的这个列表中,我正在尝试执行另一个查询,以根据第一个服务中 returned 的 CreatedBy id 获取更多用户信息,以恢复在 CreatedBy 中具有 ID 的那些用户的名称。

var userDtos = _mapper.Map<List<ApplicationUser>, List<UserDetailsDto>>(users);

foreach (UserDetailsDto user in userDtos)
{
    user.CreatedByName = await _userService
        .SearchUsers(userDtos.Where(user.Id == user.CreatedBy))
}

到目前为止,我觉得这是一个可能的解决方案,但我不确定我将在哪里或如何提取它,因为该解决方案在我使用“.Where”语句时给我一个错误。也许如果我可以创建另一个服务,该服务将 return 通过 Id 代替用户并使用 createdby Id 来提取名称,但目前还不存在类似的服务。我想要 return 的模型也与代表用户 table 的模型有点不同,因为 ApplicationUser 具有 CreatedBy,它是一个 Id,但 returned 模型 userDetailsDto 将具有一个名称字符串 属性 以及我将尝试在自动映射器中分配的名称。如果我能想到如何通过 Id 分配名称。

CreateMap<ApplicationUser, UserDetailsDto>()
.ForMember(dest => dest.CompanyName,
           opts => opts.MapFrom(src => src.Company.Company_Name));

理想情况下,您应该能够使用导航属性解决这个问题。如果您的用户 table 使用 CreatedBy 代表 CreatedBy 用户 ID,那么您可以调整映射以促进 CreatedBy 导航 属性:

public class User
{
    public class UserId { get; set; }

    // ...

    public virtual User CreatedBy { get; set; }
}

然后在映射中为 FK 关联使用阴影 属性:(在 OnModelCreating 或使用 EntityTypeConfiguration 中)

EF 核心

.HasOne(x => x.CreatedBy)
.WithMany()
.HasForeignHey("CreatedBy") // Property on Users table
.Required();

EF 6

.HasRequired(x => x.CreatedBy)
.WithMany()
.Map(x => x.MapKey("CreatedBy")) // Property on Users table

或者,如果您希望用户可以访问 CreatedBy FK table,请将其映射为类似 CreatedByUserId:

的内容
public class User
{
    public int UserId { get; set; }

    // ...
    [ForeignKey("CreatedBy"), Column("CreatedBy")]
    public int CreatedByUserId { get; set; }
    public virtual User CreatedBy { get; set; }
}

现在,当您去搜索您的用户时,您可以一次性投射您的 CreatedBy 用户 ID 和名称。

当谈到可选的搜索参数时,您应该尽可能将条件(if/else/ null 检查等)放在 Linq 之外。这有助于编写更高效的查询,而不是将条件逻辑嵌入到 SQL.

public async Task<List<ApplicationUserViewModel>> SearchUsers(UserSearchDto userSearchDto)
{
    var query = _locationDbContext.Users.AsQueryable();

    if (!string.IsNullOrEmpty(userSearchDto.FirstName))
        query = query.Where(x => x.FirstName.Contains(userSearchDto.FirstName));
    if (!string.IsNullOrEmpty(userSearchDto.LastName))
        query = query.Where(x => x.LastName.Contains(userSearchDto.LastName));
    if (!string.IsNullOrEmpty(userSearchDto.CompanyName))
        query = query.Where(x => x.Company.Name.Contains(userSearchDto.CompanyName));

    return await query.ProjectTo<ApplicationUserViewModel>(_config)
        .ToListAsync();
}

其中 _config 反映了一个自动映射器 MapperConfiguration,其中包含有关如何将用户映射到所需视图模型的详细信息。如果您没有利用 Automapper,您可以使用 Select 来投影值来完成此操作。其他考虑因素是考虑使用 StartsWith 而不是 Contains,可能会提供一个选项来执行更昂贵的 Contains 搜索...还添加诸如最小搜索长度检查之类的东西(即 3 + 字符)和分页或结果行限制(即 Take(50)),以避免大量搜索请求破坏您的系统。 (即搜索名字中带有“e”的用户)

该视图模型可能有 UserId、UserName、CompanyName,然后是 CreatedByUserId、CreatedByName 之类的东西。要解决 CreatedBy 详细信息,您只需在 Automapper 配置或 Select(u => new ApplicationUserViewModel { ... }) 语句中引用 u.CreatedBy.UserId 和 u.CreatedBy.Name。