更改代码以提高性能(渴望加载)

时间:2019-04-14 14:58:52

标签: c# entity-framework entity-framework-core

我具有这段代码来查找UserInfo,并在UserRole中查找用户角色,并从Role中查找角色信息。

我正在使用身份表。

这是我的代码:

public virtual async Task<ActionResult<List<User>>> GetuserList(CancellationToken cancellationToken)
{
    var userDto = new UserDto();
    var users = await userManager.Users.Include(x => x.UserRoles).ThenInclude(t=>t.Role).ToListAsync();

    return users;
}

但是此代码不利于性能,因为第一步获取所有用户信息userManager.Users,第二步查找所有user role信息并再次查找所有用户信息.Include(x => x.UserRoles)并查找所有角色信息.ThenInclude(t=>t.Role)

我只需要roleID中的UserRoleroleName中的Role

我应该怎么做才能加载必填信息而不是全部?

更新代码:

我写这段代码:

var users = userManager.Users.Select(x => new UserDto
        {
            UserName = x.UserName,
            Email = x.Email,
            Family = x.Family,
            Name = x.Name,
            Password = x.PasswordHash,
            PhoneNumber = x.PhoneNumber,
            RoleId = x.UserRoles.FirstOrDefault(t => t.UserId == x.Id).UserId
        });

但是我仍然需要在RoleName上找到RoleTabel,但是它不能使用Include中的Select。我如何使用find RoleName ????

3 个答案:

答案 0 :(得分:1)

在调用ToListAsync()直到在数据源上执行查询之前,您可以使用IncludeThenIncludeSelectWhere等来塑造查询..功能。

您应该做的只是将您的第一个查询与更新中的投影结合在一起。

public virtual async Task<ActionResult<List<User>>> GetuserList(CancellationToken cancellationToken)
{
    var userDto = new UserDto();
    var users = await userManager.Users.Include(x => x.UserRoles).ThenInclude(t=>t.Role)
       .Select(x => new UserDto
        {
            UserName = x.UserName,
            Email = x.Email,
            Family = x.Family,
            Name = x.Name,
            Password = x.PasswordHash,
            PhoneNumber = x.PhoneNumber,
            RoleId = x.UserRoles.FirstOrDefault(t => t.UserId == x.Id).UserId
        })
      .ToListAsync();

    return users;
}

答案 1 :(得分:1)

要从嵌套的子表引用中进行选择,我使用的方法是选择一个匿名类型以构建有效的查询,然后再将其减少到DTO中。

例如:

var users = userManager.Users.Select(x => new 
{
    UserName = x.UserName,
    Email = x.Email,
    Family = x.Family,
    Name = x.Name,
    Password = x.PasswordHash,
    PhoneNumber = x.PhoneNumber,
    Role = x.UserRoles.Select(r => new { r.RoleId, r.Role.RoleName}).FirstOrDefault()
}).ToList()
.Select(x => new UserDto
{
    UserName = x.UserName,
    Email = x.Email,
    Family = x.Family,
    Name = x.Name,
    Password = x.PasswordHash,
    PhoneNumber = x.PhoneNumber,
    RoleId = x.Role?.RoleId,
    RoleName = x.Role?.RoleName
}).ToList();

您可以使用单个.Select来解决问题,尽管这可能会导致SQL效率降低,因为每个内部.FirstOrDefault()等都可能会重新加入UserRole / Role表。这样,第一个.Select()为包含我们需要的子数据的嵌套结构构建SQL,然后第二个对生成的POCO匿名类型进行操作以构造要返回的平面DTO。

最后一件事,当使用.First() / .FirstOrDefault()时,如果只希望一个条目中的一个条目,则应使用.OrderBy()子句以确保可预测的结果。

此外,鉴于我们正在使用.Select()来映射输出,因此您无需使用.Include()从相关实体中进行选择。您也不需要User.UserRoles中的.Where()子句。 (User.UserRoles仅包含该UserId的UserRoles。)在从DbContext连接松散关联的表时,将看到.Where()子句。最好通过映射的FK直接关联实体关系。

编辑:由于每个用户可能没有角色,因此在第二个.Select()中,您需要使用“?”。操作员检索角色ID和名称。这意味着DTO角色ID必须可以为空。如果您想默认使用RoleId为0或类似的值,则可以使用RoleId = x.Role?.RoleId ?? 0

答案 2 :(得分:0)

我有一些类似的东西,希望能有所帮助。效果很好

      if (users != null && users.Length > 0 )
        {
            foreach (var item in users)
            {
                var userRoles = await UserManager.GetRolesAsync(item);
                if (userRoles.Contains("Foreman"))
                {
                    CW.AddRange(db.CrewMemberForeman.Where(x => x.AssignedForemanId == item).Select(x => x.CrewMember).OrderBy(x => x.FirstName).ToList());
                }
            }
        }