Ядро Framwork сущности генерирует странный SQL - PullRequest
1 голос
/ 15 марта 2019

У меня есть запрос 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 изменил бы его поведение просто путем добавления дополнительных значений группы. Может кто-нибудь объяснить поведение?

1 Ответ

0 голосов
/ 15 марта 2019

Давайте сделаем шаг назад и посмотрим на это с другой точки зрения: если бы вы написали SQL-запрос вручную и хотели получить все данные, необходимые в одном запросе, вы получили бы много дублирующих данных для возможностей.и аккаунт.Вы также можете сделать это здесь:

var query = from o in Quotes select o;

var oppQuotes = from o in query
select new 
{
    AccountId = o.Opportunity.Account.Id,
    AccountName = o.Opportunity.Account.Name,
    // ... and so on, with all the fields you expect to use.
    OpportunityId = o.Opportunity.Id,
    OpportunityName = o.Opportunity.Name,
    // ... and so on, with all the fields you expect to use.
    QuoteId = o.Id,
    QuoteName = o.Name,
    // ... and again, you get the point.
};

Затем просто введите .AsEnumerable() и выполните группировку в своем коде C #.В любом случае база данных не сможет ничего оптимизировать.

var opportunityQuotes = from q in oppQuotes.AsEnumerable()
group q by new { q.AccountId, q.AccountName }
into accounts
// ... and so on.

По вашему вопросу почему EF создает странный запрос, я в растерянности.

В любом случае всегда полезно подумать о том, как ВЫ создадите SQL-код, чтобы получить данные, которые вам нужны наиболее эффективно, и не полагаться на то, что EF «делает правильные вещи».Во многих случаях это произойдет, в других это полностью взорвется на вашем лице.Когда вам нужен запрос, подумайте о SQL, а затем переведите его в код EF.Если вы скажете это конкретно, что вы хотите, вы получите это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...