我有以下实体:
public class Project {
public Int32 Id { get; set; }
public ICollection<ProjectRole> ProjectRoles { get; set; }
}
public class User {
public Int32 Id { get; set; }
public ICollection<ProjectRole> ProjectRoles { get; set; }
}
public class ProjectRole {
public Int32 ProjectId { get; set; }
public Int32 UserId { get; set; }
public String Role { get; set; }
public Project Project { get; set; }
public User User { get; set; }
}
给定一个userId
我需要找到所有项目:
1.只有一个ProjectRole
,Role
等于Admin
;
2.Admin
的UserId
等于给定的userId
。
var userId = 1;
var projects = context.Projects.Where(x => x.ProjectRoles.Count(y => y.Role == "Admin") == 1 && ??
我首先获取只有一个角色等于Admin
的所有项目。
我缺少的是确保ProjectRole的UserId
等于给定的userId
。
我该怎么做?
这个问题比最初看起来更有趣,主要是因为有许多简单的解决方案,但它们只需要为每个项目执行 2 个子查询才能满足 2 个要求。
例如,由于
x.ProjectRoles.Count(y => y.Role == "Admin")
条件确保每个项目只有 1 个管理员,附加条件如
&& x.ProjectRoles.Any(y => y.Role == "Admin" && y.UserId == userId)
或
&& x.ProjectRoles.FirstOrDefault(y => y.Role == "Admin").UserId == userId
或
&& x.ProjectRoles.Where(y => y.Role == "Admin").Select(y => y.UserId).Contains(userId)
将确保单个管理员是给定用户。
另一种方法是使用这样的标准
x.ProjectRoles.Any(y => y.Role == "Admin" && y.UserId == userId)
&& !x.ProjectRoles.Any(y => y.Role == "Admin" && y.UserId != userId)
换句话说,项目将给定的用户作为管理员,而没有其他管理员。
实际上可以删除y.UserId == userId
条件
x.ProjectRoles.Any(y => y.Role == "Admin")
&& !x.ProjectRoles.Any(y => y.Role == "Admin" && y.UserId != userId)
即项目有管理员,除了给定用户之外也没有其他管理员,因此它只有 1 个管理员,它是给定的用户。
无论如何,如开头所述,所有这些项目都为每个项目执行 2 个子查询。 现在,如果您想知道(这对我来说是有趣的部分(是否通过单个子查询实现,答案是肯定的,条件如下:
x.ProjectRoles.Where(y => y.Role == "Admin")
.Min(y => y.UserId == userId ? (int?)1 : 0) == 1
原因如下。在Where
子句之后,我们有项目的管理员集。现在表达式
.Min(y => y.UserId == userId ? 1 : 0)
会回来
null
集为空时(没有管理员(- 0 当有给定用户以外的管理员时
- 1 当给定用户是管理员并且没有其他管理员时
如我们所见,只有返回值1
满足这两个要求,因此== 1
将实现所需的过滤。
试试这个:
context.ProjectRoles
.GroupBy(p => new { p.Role, p.UserId })
.Where(g => g.Key.Role == "Admin" &&
g.Count() == 1 &&
g.All(z => z.UserId == userId)
);