[英]Get Children common to All Parents
我有以下使用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表达式:
Children
从得到它的名字ChildrenLocalization
与Language="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才能使其正常工作。
如何使查询更简单?
由于您具有多对多关系,因此最好以结果实体( Children
)为基础(启动)查询,从而避免了从另一端( Parent
)启动GroupBy
/ Distinct
的需要。
因此给定
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。
您从独特的 Children
开始。 对于要求(2),您只需使用导航属性。 要求(1)更复杂( 所有目标总是比任何目标都更难实现),但我认为标准
parents.All(p => p.ParentChildrens.Select(pc => pc.ChildrenId).Contains(c.ChildrenId))
非常直观地代表了所有父母共同的孩子 。
如果我正确理解这一点,则可能可行。 这将是一个查询。
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();
给定IQueryable<Parent> parents
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
})
假设您有3个父母,其ID为10、11、12。假设您有3个孩子,其ID为20、21、22。
ParentChildrens表:
ChildId | ParentId
20 10
20 11
20 12
21 10
21 11
22 10
22 12
所以孩子20有父母10/11/12; 儿童21的父母10/11; 孩子22的父母10/12。
“让所有父母共同拥有孩子”; 如果这意味着:在其“父母”集合中获得拥有每个可用父母的“孩子”,那么很容易看到您只想要20岁的孩子,并且只希望这个孩子一次
因为所有的父子关系都是唯一的,所以我们知道如果有X个父母,我们希望孩子具有完全X个父母。
您不需要这些子级的所有属性,只想从ChildrenLocalization中使用Language =“ en ” 获得其名称,这样的名称是否总是为零或一个?如果我们还应该采用哪个呢? ,或全名?
因为我们需要将自己限制为所有ParentCount等于父母数量的孩子,所以我们还需要计算每个孩子的Parents数量
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();
});
现在我们不想要所有这些孩子,我们只想要那些ParentCount等于Parents数量的孩子
var childrenWithAllParents = childrenWithParentCount
.Where(child => !child.ParentCount == dbContext.Parents.Count());
您是否注意到,我仅创建了IQueryable对象,但尚未执行任何查询。 要执行查询:
var result = childrenWithAllParents.ToList();
有些人喜欢用一个大的LINQ语句给其他人留下深刻的印象。 好吧,这是:
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();
幸运的是,您的数据库管理系统足够智能,可以记住父母的数量,而不必为每个孩子再次计算一次。
您需要从分组中拆分呼叫。
List<Parent> result = context.Parents
.Include(i => i.ParentChildrens)
.ThenInclude(i => i.Children)
.ThenInclude(i => i.ChildrenLocalizations)
.ToList();
var finalResult = result.SelectMany(c => c.ParentChildrens, (o, j) =>
{
return new
{
Id = j.ChildrenId,
Parent = o.ParentId,
Name = j.Children.ChildrenLocalizations.First(c => c.Language == "en").Name
};
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.