繁体   English   中英

使用联接防止Entity Framework查询中的SELECT N + 1问题

[英]Prevent SELECT N+1 issue in Entity Framework query using Joins

我正在尝试从间接相关的实体中查询某些内容到单一用途的视图模型中。 这是我实体的复制品:

public class Team {
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Member> Members { get; set; }
}

public class Member {
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Pet {
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
    public Member Member { get; set; }
}

每个类都在我的数据库上下文中的DbSet<T>中。

这是我想从查询中构造的视图模型:

public class PetViewModel {
    public string Name { get; set; }
    public string TeamItIndirectlyBelongsTo { get; set; }
}

我这样做与这个查询:

public PetViewModel[] QueryPetViewModel_1(string pattern) {
    using (var context = new MyDbContext(connectionString)) {
        return context.Pets
            .Where(p => p.Name.Contains(pattern))
            .ToArray()
            .Select(p => new PetViewModel {
                Name = p.Name,
                TeamItIndirectlyBelongsTo = "TODO",
            })
            .ToArray();
    }
}

但是显然那里仍然有一个“ TODO”。

Gotcha:目前我无法更改实体 ,因此我不能仅在Member上包括List<Pet>属性或Team属性来提供帮助。 我现在想在查询中修复问题。

这是我当前的解决方案:

public PetViewModel[] QueryPetViewModel_2(string pattern) {
    using (var context = new MyDbContext(connectionString)) {
        var petInfos = context.Pets
            .Where(p => p.Name.Contains(pattern))
            .Join(context.Members,
                p => p.Member.Id,
                m => m.Id,
                (p, m) => new { Pet = p, Member = m }
            )
            .ToArray();

        var result = new List<PetViewModel>();

        foreach (var info in petInfos) {
            var team = context.Teams
                .SingleOrDefault(t => t.Members.Any(m => m.Id == info.Member.Id));

            result.Add(new PetViewModel {
                Name = info.Pet.Name,
                TeamItIndirectlyBelongsTo = team?.Name,
            });
        }

        return result.ToArray();
    }
}

但是,这存在“ SELECT N + 1”问题。

有没有一种方法可以只创建一个 EF查询来获得所需的结果,而无需更改实体?

PS。 如果您希望包含上述内容的“即插即用”复制品,请参见此要点

通过不提供必要的导航属性,使事情变得更加困难,正如注释中提到的@Evk不会影响数据库的结构,但是当您编写诸如pet.Member.Team.Name类的pet.Member.Team.Name时,允许EF提供必要的联接pet.Member.Team.Name (这里需要什么)。

模型的另一个问题是您既没有从TeamPet的导航路径,也没有从PetTeam的导航路径,因为“加入的”实体Member没有导航属性。

仍然可以通过一些不那么直观的方式使用现有的导航属性和不寻常的join运算符来获得单个查询所需的信息,如下所示:

var result = (
    from team in context.Teams
    from member in team.Members
    join pet in context.Pets on member.Id equals pet.Member.Id
    where pet.Name.Contains(pattern)
    select new PetViewModel
    {
        Name = pet.Name,
        TeamItIndirectlyBelongsTo = team.Name
    }).ToArray();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM