如何使用 Linq 展平 Entity Core 多对多集合

How to flatten Entity Core many-to-many collections using Linq

我不知道如何展平多对多集合。这些实体用于使用 Entity Framework 核心创建的身份表。它们包含我手动添加的导航属性,因为身份实体默认不包含这些(以及未显示的自定义模型构建器代码)

public class AppUser : IdentityUser<long> {

    public string FirstName { get; set; }
    public string LastName { get; set; }

    public virtual List<AppUserRole> UserRoles { get; set; } = new List<AppUserRole>();

}

public class AppRole : IdentityRole<long> {

    public virtual List<AppUserRole> UserRoles { get; set; } = new List<AppUserRole>();

}

public class AppUserRole : IdentityUserRole<long> {

    public virtual AppUser User { get; set; }
    public virtual AppRole Role { get; set; }

}

这是我的 repo 中的调用:

public async Task<IEnumerable<AppUser>> GetAllWithRoles() 
{
    return await _dbSet
        .AsNoTracking()
        .Include(u => u.UserRoles)
        .ThenInclude(ur => ur.Role)
        .ToListAsync();
}

上面调用returns返回这个结构:

[
{
    "firstName": "XXXX",
    "lastName": "YYYYY",
    "userRoles": [
        {
            "role": {
                "userRoles": [],
                "id": 1,
                "name": "xxx",
                "normalizedName": "xxxx",
                "concurrencyStamp": "1617fe40-77e2-46cb-9c1c-df597d09775c"
            },
            "userId": 1,
            "roleId": 1
        }
    ]       
}
]

我要的是这个:

[
{
    "firstName": "Alex",
    "lastName": "Florin",
    "RoleName": "Role1",
},
{
    "firstName": "Alex",
    "lastName": "Florin",
    "RoleName": "Role2",
},
{
    "firstName": "Jon",
    "lastName": "Smith",
    "RoleName": "Role1",
},
]

我想要的是扁平化集合。我研究过 SelectMany 但我不知道如何将它用于多对多集合,因为我对 Linq 还很陌生。而且我知道调用函数的类型需要更改为与我所需结构匹配的视图模型。

Automapper 是另一个选项,因为我正在使用它为我的更简单的实体创建视图模型,但我不清楚如何设置使用它来展平多对多关系

不确定您想要将什么完全展平,Select 来自用户

的所有角色
users.Select(p => p.UserRoles.Role)

至 Select 角色

的所有用户
roles.Select(p => p.UserRoles.User)

编辑:

我想这个例子会对你有所帮助https://blog.oneunicorn.com/2017/09/25/many-to-many-relationships-in-ef-core-2-0-part-2-hiding-as-ienumerable/

您的模型可能看起来像这样

public class AppUser : IdentityUser<long> {

    public string FirstName { get; set; }
    public string LastName { get; set; }

    private ICollection<AppRole> UserRoles{ get; } = new List<PostTag>();

    [NotMapped]
    public IEnumerable<string> RoleNames => UserRole.Role.Name

}

public class AppRole : IdentityRole<long> {
    private ICollection<AppRole> UserRoles{ get; } = new List<PostTag>();

    [NotMapped]
    public IEnumerable<AppRole> Users => UserRole.User
}

public class AppUserRole : IdentityUserRole<long> {
    public long UserID{ get; set; }
    public AppUser User { get; set; }

    public long RoleID { get; set; }
    public Role Role { get; set; }
}

我明白了。一个问题是它排除了 UserRoles = null 的任何用户,但由于所有用户都保证在我们的系统中至少拥有一个角色,所以应该没问题。

    public async Task<IEnumerable<UserEditorViewModel>> GetAllWithRoles() {

        return await _dbSet
            .AsNoTracking()
            .SelectMany(u => u.UserRoles)
            .Select(ur => new UserEditorViewModel {
                FirstName = ur.User.FirstName,
                LastName = ur.User.LastName,
                RoleName = ur.Role.Name
            })
            .ToListAsync();
    }