我正在尝试使用实体框架和linq以统一的方式获取m:m表中的所有记录。
数据模型:
public partial class Group
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid GroupId { get; set; }
[StringLength(100)]
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
public partial class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid UserId { get; set; } = new Guid();
[StringLength(100)]
public string FirstName { get; set; }
[StringLength(100)]
public string LastName { get; set; }
[StringLength(255)]
public string DisplayName { get; set; }
public virtual ICollection<Group> Groups { get; set; }
}
以下是SQL中的查询,该查询会拉回我要查找的数据集:
select g.GroupId, g.[Name], u.DisplayName, u.UserId
from [Group] g, [User] u, [GroupUsers] gu
where g.GroupId = gu.Group_GroupId
and gu.User_UserId = u.UserId
order by g.[Name]
这是我在Linq中正在尝试的操作,但是出现了自引用循环错误:
using (RequestContext ctx = new RequestContext())
{
return ctx.Groups.SelectMany(x => x.Users).Include(x => x.Groups).ToList();
}
这似乎应该比较容易,但是我发现Entity Framework中的m:m有点棘手。任何帮助,将不胜感激。
答案 0 :(得分:1)
很高兴您将多对多定义为两个虚拟集合,而没有指定连接表!
您确实意识到,如果不获取“与他们的用户组成的组”,而是查询组和用户的左外部连接(您称其为扁平形式),则组属性将一遍又一遍地重复再次为每个用户?
但是,这是您的决定,您必须说服项目负责人,将相同的组属性传输一百次而不是仅传输一次更好。
您是对的,对于左外部连接,您需要执行SelectMany
。我不确定您为什么决定使用Include
而不是Select
。
查询数据时,始终使用选择,并仅选择您实际打算使用的属性。如果您打算更新提取的包含数据,请仅使用包含。
这样做的原因在一对多关系中特别有意义。如果您获取“学校及其学生”,并且ID为4的学校有1000名学生,那么您知道该学校的每个学生都将拥有一个值为4的外键。将这个值4转移1000次是很浪费的!
Enumerable.SelectMany的重载之一具有参数resultSelector
,它将以一个Group
和一个User
作为输入来创建结果。此版本非常适合您的需求
var result = dbContext.Groups.SelectMany(
group => group.Users,
(group, user) => new
{
// Select the Group properties you plan to use
GroupId = group.GroupId,
GroupName = group.Name,
...
// Select the User properties you plan to use
UserId = user.UserId,
UserName = user.DisplayName,
...
})
// if desired do some ordering
.OrderBy(joinedItem => joinedItem.GroupName);