【问题标题】:I need to do a query on a returned list of users from another query, by Id我需要按 Id 对另一个查询返回的用户列表进行查询
【发布时间】:2021-09-23 23:48:07
【问题描述】:

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

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

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();
}

因此,在我返回的这个列表中,我试图根据第一个服务中返回的 CreatedBy id 来获取更多用户信息,以返回那些在 CreatedBy 中具有 id 的用户的名称。

p>
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”语句的时候给了我一个错误。也许如果我可以创建另一个服务,该服务将通过 Id 返回用户并使用 createdby Id 来提取名称,但还没有类似的东西存在。我想返回的模型也与表示 Users 表的模型有点不同,因为 ApplicationUser 具有 CreatedBy 这是一个 Id 但返回的模型 userDetailsDto 也将具有一个名称字符串属性,我将尝试在此处分配在自动映射器中。如果我能想到如何通过 Id 分配名称。

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

【问题讨论】:

  • 向我们展示User 模型的代码(DB 模型,而不是 DTO)
  • 试试ProjectTo 而不是Map

标签: c# entity-framework .net-core linq-method-syntax


【解决方案1】:

理想情况下,您应该能够使用导航属性解决此问题。如果您的 User 表使用 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

或者,如果您希望在 User 表中访问 CreatedBy FK,请将其映射为 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 =&gt; new ApplicationUserViewModel { ... }) 语句中引用 u.CreatedBy.UserId 和 u.CreatedBy.Name。

【讨论】:

  • 谢谢,我不确定这是我的经验上升还是您的描述清楚但这是迄今为止我收到的关于 Stack Overflow 的问题的最佳答案,谢谢你
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-04-20
  • 2019-09-29
  • 2023-02-09
  • 2019-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多