问题描述
目前,我有一个从 User 表中获取用户信息的服务。用户可以由管理员或员工创建,所有这些员工都有自己的 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 的那些用户的名称。>
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));
解决方法
理想情况下,您应该能够使用导航属性解决此问题。如果您的用户表使用 CreatedBy 来表示 CreatedBy 用户 ID,那么您可以调整您的映射以促进 CreatedBy 导航属性:
public class User
{
public class UserId { get; set; }
// ...
public virtual User CreatedBy { get; set; }
}
然后在映射中为 FK 关联使用 shadow 属性:(在 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 检查等)放在之外。这有助于组合更高效的查询,而不是将条件逻辑嵌入到 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。