让所有父母共同的孩子

问题描述 投票:1回答:5

我有以下使用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; }
}

鉴于我需要使用qqxswpoi,使用Linq to Entities lambda表达式:

  1. 让所有父母共同的孩子;
  2. 每个IQueryable<Parent>的名字来自ChildrenChildrenLocalization

所以我尝试了以下方法:

Language="en"

此查询给出了预期的结果,但它看起来太复杂了。

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

如何使查询更简单?

c# linq entity-framework-core linq-to-entities entity-framework-core-2.2
5个回答
3
投票

由于你有多对多的关系,最好在结果实体(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(); )上建立查询(开始),因此如果你从另一端(Children)启动它,就不需要GroupBy / Distinct

所以给定

Parent

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

IQueryable<Parent> parents

很好地转换为单个SQL。

你从独特的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() }); 开始。对于要求(2),您只需使用导航属性。要求(1)更复杂(所有要求总是比任何更难),但我认为标准

Children

非常直观地代表所有父母共同的孩子。


1
投票

如果我理解正确,这可能会奏效。这将是一个单一的查询。

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

1
投票

假设你有3个父母,Id 10,11,12假设你有3个孩子,身份证号为20,21,22

父母儿童餐桌:

    var result =
            (from parent in context.Parents
            from pToC in parent.ParentChildrens
            where pToC.Children.ParentChildrens.Select(pc => pc.ParentId).Distinct().Count() == context.Parents.Count()
            from childLocation in pToC.Children.ChildrenLocalizations
            where childLocation.Language == "en"
            select new { pToC.Children.ChildrenId, childLocation.Name }).Distinct();

所以20岁的孩子有父母10/11/12; 21岁儿童有父10/11; 22岁的孩子有父母10/12。

“让所有父母共同的孩子”;如果这意味着:在其父母集合中获取拥有所有可用父项的子项,那么很容易看出您只需要Child 20,并且您只希望这个Child只有一次

因为所有父子关系都是唯一的,我们知道如果有X父母,我们希望孩子们拥有正确的X父母。

你不想要这些孩子的所有属性,你只想“从ChildrenLocalization with Language =”en获取它的名字,是否总是零或一个这样的名字?如果还有更多的我们应该采取什么?任何名字或者所有的名字?

因为我们需要限制所有父母数量等于父母数量的孩子,我们还需要计算每个孩子的父母数量

ChildId | ParentId
  20         10
  20         11
  20         12
  21         10
  21         11
  22         10
  22         12

现在我们不想要所有这些孩子,我们只希望那些ParentCount等于Parent数量的孩子

var childrenWithParentCount = dbContext.Children.Select(child => new
{
    // "get its name from ChildrenLocalization with Language="en"
    LocalizationName = child.ChildrenLocalizations
                            .Where(localization => localization.Language == "en")
                            .Select(localization => localizaition.Name)
                            .FirstOrDefault();

    // or if you want all names:
    LocalizationNames = child.ChildrenLocalizations
                             .Where(localization => localization.Language == "en")
                             .Select(localization => localizaition.Name)
                            .ToList;

    ParentCount = child.ParentChildren
                       .Select(parentChild => parentChild.ParentId)
                       .Count();
});

您是否注意到,我只创建了IQueryable对象,但我还没有执行任何查询。要执行查询:

var childrenWithAllParents = childrenWithParentCount
    .Where(child => !child.ParentCount == dbContext.Parents.Count());

有些人喜欢用一个大的LINQ声明来打动别人;这里它是:

var result = childrenWithAllParents.ToList();

幸运的是,您的数据库管理系统足够智能,可以记住父母的数量,而不是每个孩子再次计算一次。


1
投票

鉴于var result = dbContext.Children.Select(child => new { LocalizationName = child.ChildrenLocalizations .Where(localization => localization.Language == "en") .Select(localization => localizaition.Name) .FirstOrDefault(); ParentCount = child.ParentChildren .Select(parentChild => parentChild.ParentId) .Count(); }) .Where(child => !child.ParentCount == dbContext.Parents.Count()) .ToList();

IQueryable<Parent> parents

-1
投票

您需要从分组中拆分您的呼叫。

parents
.SelectMany(p => p.ParentChildrens)
.Select(pc => pc.Children)
.Where(c => c.ParentChildrens
    .Select(pc => pc.ParentId)
    .OrderBy(i => i)
    .SequenceEqual(parents.Select(p => p.ParentId).OrderBy(i => i)))
.Select(c => new
{
    Id = c.ChildrenId,
    c.ChildrenLocalizations.FirstOrDefault(cl => cl.Language == "en").Name
})
© www.soinside.com 2019 - 2024. All rights reserved.