Red*_*one 4 c# entity-framework-core
在我的数据库中,我有两个表Organizations和OrganizationMembers,具有 1:N 关系。
我想表达一个查询,该查询返回每个组织的第一个组织所有者的名字和姓氏。
我当前的选择表达式有效,但它既不高效也不适合我,因为每个子查询都被多次定义。
await dbContext.Organizations
.AsNoTracking()
.Select(x =>
{
return new OrganizationListItem
{
Id = x.Id,
Name = x.Name,
OwnerFirstName = (x.Members.OrderBy(member => member.CreatedAt).First(member => member.Role == RoleType.Owner)).FirstName,
OwnerLastName = (x.Members.OrderBy(member => member.CreatedAt).First(member => member.Role == RoleType.Owner)).LastName,
OwnerEmailAddress = (x.Members.OrderBy(member => member.CreatedAt).First(member => member.Role == RoleType.Owner)).EmailAddress
};
})
.ToArrayAsync();
Run Code Online (Sandbox Code Playgroud)
是否可以以某种方式总结或重用子查询,所以我不需要多次定义它们?
请注意,我已经尝试将子查询结果存储在变量中。这不起作用,因为它需要将表达式转换为语句体,这会导致编译器错误。
可以通过引入中间投影 ( Select)来重用子查询,这相当于let查询语法中的运算符。
例如:
dbContext.Organizations.AsNoTracking()
// intermediate projection
.Select(x => new
{
Organization = x,
Owner = x.Members
.Where(member => member.Role == RoleType.Owner)
.OrderBy(member => member.CreatedAt)
.FirstOrDefault()
})
// final projection
.Select(x => new OrganizationListItem
{
Id = x.Organization.Id,
Name = x.Organization.Name,
OwnerFirstName = Owner.FirstName,
OwnerLastName = Owner.LastName,
OwnerEmailAddress = Owner.EmailAddress
})
Run Code Online (Sandbox Code Playgroud)
请注意,在 EF Core 3.0 之前的版本中FirstOrDefault,First如果您想避免客户端评估,则必须使用而不是。
此外,这不会使生成的 SQL 查询更好/更快 - 它仍然包含针对最终选择中包含的每个属性的单独内联子查询。因此会提高可读性,但不会提高效率。
这就是为什么通常最好将嵌套对象投影到未展平的 DTO 属性中,即,而不是OwnerFirstName, OwnerLastName,OwnerEmailAddress有一个具有属性的类FirstName,LastName,EmailAddress和属性让说Owner该类型OrganizationListItem(类似于具有参考导航属性的实体)。这样你就可以使用类似的东西
dbContext.Organizations.AsNoTracking()
.Select(x => new
{
Id = x.Organization.Id,
Name = x.Organization.Name,
Owner = x.Members
.Where(member => member.Role == RoleType.Owner)
.OrderBy(member => member.CreatedAt)
.Select(member => new OwnerInfo // the new class
{
FirstName = member.FirstName,
LastName = member.LastName,
EmailAddress = member.EmailAddress
})
.FirstOrDefault()
})
Run Code Online (Sandbox Code Playgroud)
不幸的是,在 3.0 之前的版本中,EF Core 将为这个 LINQ 查询生成 N + 1 个 SQL 查询,但在 3.0+ 中,它将生成一个非常有效的 SQL 查询。