如何将两个连接表中的 IQueryable LINQ 结果返回到 List<string> 中?
Posted
技术标签:
【中文标题】如何将两个连接表中的 IQueryable LINQ 结果返回到 List<string> 中?【英文标题】:How to return IQueryable LINQ result from two joined tables into a List<string>? 【发布时间】:2021-03-23 21:34:55 【问题描述】:这是一个附加问题:Entity Framework Core 5.0 How to convert LINQ for many-to-many join to use Intersection table for ASP.NET Membership
如何将RoleName
列的以下LINQ IQueryable
结果(来自两个连接表)的结果返回到List<string>
?
var queryResult = (this.DbContext.aspnet_UsersInRoles
.Where(x => x.UserId == dpass.UserId)
.Join(
this.DbContext.aspnet_Roles,
ur => ur.RoleId,
r => r.RoleId,
(ur, role) => new
ur,
role
)
.Select(x => new x.ur.UserId, x.role.RoleName )
);
更新 1
我需要值数组形式的List
,以便我可以使用Contains()
方法。我需要搜索分配给UserId
的特定RoleNames
。如果我在IQueryable
上使用ToList()
,那么数组结果的形式是:
RoleName = "admin"
角色名 = “用户”
我无法使用.Contains()
方法,因为我收到以下错误:
无法从 'string' 转换为
<
anonymous 类型:字符串 RoleName>
。
似乎期望可以将查询结果分配给一个类。但是,一个不存在,因为我正在执行此操作。
更新 2
我需要列表中的queryResult
,格式为:
“管理员”
“用户”
有了这个输出,我可以使用.Contains()
方法来执行多项检查。这用于确定Windows Forms
字段属性。因此,如果UserId
属于admin
角色,则表单启用某些复选框和单选按钮,而如果UserId
属于user
角色,则表单启用不同的复选框。这不是可用角色的详尽列表以及表单执行的检查。但是,重要的是需要在单独的 IF 语句中执行对 List
的多项检查。
目前,我可以使用queryResult
执行以下操作:
-
获取
RoleNames
的列表
通过检查特定的RoleName
对queryResult
执行单独的LINQ 查询
执行.Count() > 0
检查以查看UserId
是否处于特定角色。
这似乎是一个丑陋的 hack,因为我有创建 1 + N
变量以通过 LINQ 检索并存储每个 RoleName
的中间步骤,然后检查 .Count()
是否大于零。我认为List
方法会更干净、更高效。如果可以的话。
var varUser = from d in queryResult
where d.RoleName == "user"
select new d.RoleName ;
var varAdmin = from u in queryResult
where u.RoleName == "admin"
select new u.RoleName ;
//... more declarations and LINQs ...
【问题讨论】:
我提供了我的问题的更新,提供了额外的上下文。 在计量化后使用GroupBy(x=> x.UserId),然后U可以在角色列表中搜索 使用导航属性,简单多了。 【参考方案1】:简答: 仅选择 RoleName,并使用 SelectMany 而不是 Select
更好的答案
所以你有一个Roles
的表和一个Users
的表(我正在简化你的长标识符,不是问题的一部分,也不是太多的输入)。
Roles
和Users
之间似乎存在多对多关系:每个Role
是零个或多个Users
的角色,每个User
都有零个或多个角色。
这种多对多关系是使用标准联结表实现的:UsersInRoles
。这个联结表有两个外键:一个给用户,一个给角色。
您有一个 UserId,并且您似乎想要拥有此 ID 的用户的所有角色的所有名称。
这个怎么样:
int userId = ...
// Get the names of all Roles of the User with this Id
var namesOfRolesOfThisUser = dbContext.UsersInRoles
// only the user with this Id:
.Where(userInRole => userInRole.UserId == userId)
// get the names of all Roles for this userInRole
.SelectMany(userInRole => dbContext.Roles.Where(role => role.RoleId == userInRole.RoleId)
.Select(role => role.RoleName));
简而言之:从 UsersInRoles 表中,仅保留属性 UserId 的值等于 userId 的那些 UsersInRoles。
从剩余的每个 UsersInRoles 中,选择具有与 UserInRole.RoleId 相等的 RoleId 的所有角色。从这些角色中获取 RoleName。
我使用SelectMany
来确保我得到一个字符串序列,而不是一系列字符串。
如果您怀疑有双重角色名称,请考虑在末尾附加 Distinct()
。
但我想加入!
有些人真的很喜欢自己做连接。
int userId = ...
var namesOfRolesOfThisUser = dbContext.UsersInRoles
.Where(userInRole => userInRole.UserId == userId)
.Join(dbContext.Roles,
userInRole => userInRole.RoleId, // from every UserInRole take the foreign key
role => role.RoleId, // from every Role take the primary key
// when they match, take only the name of the Role
(userInRole, role) => role.RoleName);
【讨论】:
简单、干净、优雅、高效。感谢您的解决方案和解释清楚的答案-您是一位绅士和学者。Hat tip
.【参考方案2】:
尝试使用 GroupBy()。注意,这种方法不支持直接IQueryable
到SQL的转换。如果您尝试在.ToList()
之前调用GroupBy()
,则会抛出错误。
在您的示例中,您可以这样做:在内存中选择一个列表,然后使用它:
var queryResult = (this.DbContext.aspnet_UsersInRoles
.Where(x => x.UserId == dpass.UserId)
.Join(this.DbContext.aspnet_Roles,
ur => ur.RoleId,
r => r.RoleId,
(ur, role) => new ur, role
)
.Select(x => new x.ur.UserId, x.role.RoleName )
.ToList() // MATERIALIZE FIRST
.GroupBy(x => x.UserId) //ADD THIS
);
queryResult.Contains(roleName=> roleName == "ROLE_TO_SEARCH")
var userId = queryResult.Key;
【讨论】:
答案不是我想要的。我已经更新了我的问题。以上是关于如何将两个连接表中的 IQueryable LINQ 结果返回到 List<string> 中?的主要内容,如果未能解决你的问题,请参考以下文章
实体框架,如何将 IQueryable 与多个 where 转换为 SQL 一起使用?