首页 > 解决方案 > 让所有父母共同拥有孩子

问题描述

我有以下使用 Entity Framework Core 的实体:

public class Parent {
  public Int32 ParentId { get; set; }
  public virtual Collection<ParentChildren> ParentChildrens { get; set; }
}

public class ParentChildren {
  public Int32 ParentId { get; set; }
  public Int32 ChildrenId { get; set; }
  public virtual Parent Parent { get; set; }
  public virtual Children Children { get; set; }
}

public class Children {
  public Int32 ChildrenId { get; set; }
  public virtual Collection<ParentChildren> ParentChildrens { get; set; }
  public virtual Collection<ChildrenLocalization> ChildrenLocalizations { get; set; }
}

public class ChildrenLocalization {
  public Int32 ChildrenId { get; set; }
  public String Language { get; set; }
  public String Name { get; set; }
  public virtual Children Children { get; set; }
}

鉴于IQueryable<Parent>我需要,使用 Linq to Entities lambda 表达式:

  1. 让所有父母共有的孩子
  2. 对于每一个都从with中Children获得它的名字。ChildrenLocalizationLanguage="en"

所以我尝试了以下方法:

var result = context.Parents
  .SelectMany(y => y.ParentChildrens)
  .GroupBy(y => y.ParentId)
  .Where(y => 
     context.Parents
       .SelectMany(y => y.ParentChildrens)
       .Select(z => z.ChildrenId)
       .Distinct()
       .All(z => y.Any(w => w.ChildrenId == z)))
  .SelectMany(y => y)
  .Select(y => new {
    Id = y.ChildrenId,
    Name = y.Children.ChildrenLocalizations.Where(z => z.Language == "en").Select(z => z.Name).FirstOrDefault()
  })
  .GroupBy(x => x.Id)
  .Select(x => x.FirstOrDefault())
  .ToList();

这个查询给出了预期的结果,但它似乎太复杂了。

我无法改进它,例如,我需要添加最后一个 GroupBy 才能使其工作。

如何使我的查询更简单?

标签: c#linqentity-framework-corelinq-to-entitiesentity-framework-core-2.2

解决方案


由于您具有多对多关系,因此最好将查询基于(开始)结果实体(Children),从而避免GroupBy/Distinct如果您从另一端(Parent)开始查询。

所以给定

IQueryable<Parent> parents

并假设您可以访问上下文,查询可以编写如下:

var query = context.Set<Children>()
    .Where(c => parents.All(p => p.ParentChildrens.Select(pc => pc.ChildrenId).Contains(c.ChildrenId)))
    .Select(c => new
    {
        Id = c.ChildrenId,
        Name = c.ChildrenLocalizations.Where(cl => cl.Language == "en").Select(cl => cl.Name).FirstOrDefault()
    });

这很好地转换为单个 SQL。

你从unique Children开始。对于要求 (2),您只需使用导航属性。要求(1)更复杂(所有总是比任何更难实现),但我认为标准

parents.All(p => p.ParentChildrens.Select(pc => pc.ChildrenId).Contains(c.ChildrenId))

相当直观地代表了所有父母共有的孩子


推荐阅读