首页 > 解决方案 > EF Code First-从多到多表返回记录

问题描述

我正在尝试使用实体框架和 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();
}

这似乎应该相对容易,但我发现实体框架中的 m:m 可能有点棘手。任何帮助,将不胜感激。

标签: c#entity-frameworklinqmany-to-many

解决方案


我很高兴您将多对多定义为两个虚拟集合,而没有指定连接表!

您确实意识到,如果您不获取“包含用户的组”,而是查询组和用户的左外连接(您称其为平面方式),则组属性将一遍又一遍地重复用户?

但是,嘿,那是你的决定,你要让你的项目负责人相信最好将相同的组属性转移一百次,而不是只转移一次。

你是对的,对于左外连接,你需要做一个SelectMany. 我不确定你为什么决定使用Include而不是Select.

查询数据时始终使用Select,并仅选择您实际计划使用的属性。仅当您计划更新获取的包含数据时才使用Include 。

这样做的原因在一对多关系中特别有意义。如果您获取“Schools with their Students”,并且 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);

推荐阅读