首页 > 解决方案 > 如何选择实体框架多对多查询?

问题描述

我的实体框架上下文中有一个多对多的关系。

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public IList<UserRole> UserRoles { get; set; }
}

public class UserRole
{
    public int UserId { get; set; }
    public int RoleId { get; set; }
}

public class Role
{
    public int Id { get; set; }
    public string Name { get; set; }
    public IList<UserRole> UserRoles { get; set; }
}

我的背景是:

public class MyContext : DbContext
{       
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<UserRole>().HasKey(sc => new { sc.UserId, sc.RoleId });
    }

    public DbSet<User> Users { get; set; }
    public DbSet<Role> Roles { get; set; }
    public DbSet<UserRole> UserRoles { get; set; }
}

所以当一个用户被分配到一个角色时,记录在 UserRole 表中。我的角色表有 4 行数据:

[
    { roleId = 1, name = "Administrator" },
    { roleId = 2, name = "Editor"},
    { roleId = 3, name = "x"},
    { roleId = 4, name = "y"}
]

但我想为用户选择所有角色。但是用户分配的数据属性应该如下所示。例如,我想为userid = 1. 因为分配了 2 个角色。

roles = [
    { roleId = 1, name = "Administrator", isAddigned = true },
    { roleId = 2, name = "Editor", isAddigned = true },
    { roleId = 3, name = "x", isAddigned = false },
    { roleId = 4, name = "y", isAddigned = false }
]

Bot 如何使用实体框架选择此查询?

标签: entity-frameworkentity-framework-coremany-to-many

解决方案


从听起来你有一个用户(ID #1)当前分配了 2 个角色,管理员和编辑器。如果用户具有该角色分配,则您希望列出所有可用的角色,如果用户分配了该角色,则“已分配”标志设置为 True,如果没有,则设置为 False。

首先,您的上下文中可能不需要 UserRoles DbSet。建议只为被视为顶级实体(您将查询的内容)的实体创建 DbSet,并依靠关系来管理其余部分。

如果您的目标是返回要与用户关联的角色列表,并指示用户是否持有这些角色(例如角色分配屏幕)。

如果您在角色上维护 UserRoles:

var roles = context.Roles
    .Select(x => new RoleViewModel
    {
       RoleId = x.RoleId,
       Name = x.Name,
       IsAssigned = x.UserRoles.Any(ur => ur.UserId == userId)
    }).ToList();

return roles;

当面对单向关联时(即用户包含 UserRoles,但 Roles 不包含),简单的第一步是获取用户分配的角色 ID,然后将其用作检查完整的角色列表。它需要 2 个简单的查询。

var usersRoleIds = context.Users
    .Where(x => x.UserId == userId)
    .SelectMany(x => x.UserRoles.Select(ur => RoleId))
    .ToList();
var roles = context.Roles
    .Select(x => new RoleViewModel
    {
       RoleId = x.RoleId,
       Name = x.Name,
       IsAssigned = userRoleIds.Contains(x.RoleId)
    }).ToList();

return roles;

推荐阅读