EF代码从多对多表的第一个返回记录

时间:2019-02-05 17:45:10

标签: c# entity-framework linq many-to-many

我正在尝试使用实体框架和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有点棘手。任何帮助,将不胜感激。

1 个答案:

答案 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);