【发布时间】:2019-03-16 00:49:52
【问题描述】:
我有一个正在尝试优化的现有 LINQ 查询。我有以下实体类型(简体)
public class Account
{
public int Id { get; set; }
public IEnumerable<OpportunityInfo> Opportunities { get; set; }
}
public class Opportunity
{
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public IEnumerable<Quote> Quotes { get; set; }
}
public class Quote
{
}
这是一个标准的账户到报价机会层次结构。没什么特别的。我在 ASP.NET Core 控制器索引方法上使用了以下查询。我从报价开始并向后工作,因为查询和机会报价之间存在动态查询逻辑,必须基于报价。否则我会从顶部开始。
var query = from o in Quotes select o;
额外的查询逻辑(过滤和排序)
var opportunityQuotes = from o in query
group o by new
{
accountId = o.Opportunity.AccountId,
accountName = o.Opportunity.Account.Name,
active = o.Opportunity.Account.Active,
}
into p
select new
{
Id = p.Key.accountId,
Name = p.Key.accountName,
Active = p.Key.active,
Opportunities =
(from q in p
group q by new
{
Id = q.Opportunity.Id,
Name = q.Opportunity.Name,
Active = q.Opportunity.Active
}
into r
select new
{
Name = r.Key.Name,
Id = r.Key.Id,
Active = r.Key.Active,
Quotes = r
})
};
opportunityQuotes.Dump();
此查询生成以下 SQL。
SELECT [o].[Id], [o].[ARRValue], [o].[AccountId], [o].[AdjustedArr], ...
FROM [Quotes] AS [o]
LEFT JOIN [Opportunities] AS [o.Opportunity] ON [o].[OpportunityId] = [o.Opportunity].[Id]
INNER JOIN [Accounts] AS [o.Account] ON [o].[AccountId] = [o.Account].[Id]
ORDER BY [o].[AccountId], [o.Account].[Name], [o.Account].[Active]
GO
SELECT [q.Opportunity0].[Id], [q.Opportunity0].[Name], [q.Opportunity0].[Active]
FROM [Opportunities] AS [q.Opportunity0]
GO
SELECT [q.Opportunity0].[Id], [q.Opportunity0].[Name], [q.Opportunity0].[Active]
FROM [Opportunities] AS [q.Opportunity0]
GO
SELECT [q.Opportunity0].[Id], [q.Opportunity0].[Name], [q.Opportunity0].[Active]
FROM [Opportunities] AS [q.Opportunity0]
GO
实际上,它会针对每个机会生成查询,但为了简洁起见,我将其省略了。我认为 EF 不应为每个报价生成单独的查询。其实如果我把查询中的 .Name 和 .Active 键参数注释掉如下图:
group q by new
{
Id = q.Opportunity.Id,
// Name = q.Opportunity.Name,
// Active = q.Opportunity.Active
}
并在 select 子句中注释掉相应的变量,它会生成更清晰的 sql。
SELECT [o].[Id], [o].[ARRValue], [o].[AccountId], ...
FROM [Quotes] AS [o]
LEFT JOIN [Opportunities] AS [o.Opportunity] ON [o].[OpportunityId] = [o.Opportunity].[Id]
INNER JOIN [Accounts] AS [o.Account] ON [o].[AccountId] = [o.Account].[Id]
ORDER BY [o].[AccountId], [o.Account].[Name], [o.Account].[Active]
GO
我感到困惑的原因是 .Name 和 .Active 在完全相同的对象中,它们以与 .Id 字段相同的方式分组在键中,因此我不明白为什么 EF 会改变它行为只需添加额外的组值。有人可以解释这种行为吗?
【问题讨论】:
-
为什么需要
.Include()电话?据我所知,您正在从所有对象中加载所需的一切。也许,这就是导致问题的原因。另外,您的问题中的第二个Opportunity课程在做什么?其中一个有不同的名称,还是我们可以忽略一个? -
我可能不需要包含。让我看看这是否有帮助。当我写下这个问题时,第二个 Opportunity 课程只是一个错误。感谢您指出。
-
我返回并删除了 .Include() ,它对生成的 SQL 没有任何影响。感谢您的建议。
-
你真的直接从
Quote引用Account实体,还是从Account->Opportunity->Quote的层次结构?如果您有直接从报价到帐户的引用,也从通过机会到帐户的报价间接引用,它可能会分散 EF 的注意力。我目前正在尝试在本地重现该行为。 -
嗯,刚刚注意到我们的名字。 “herold”正在回答“国王”:-)
标签: c# asp.net-core entity-framework-core