0

Упрощенный пример: У меня есть таблица с FirstName, LastName. Я заинтересован в том, чтобы вернуть всех людей, чье полное приветствие не больше на N, отсортированное по длине. Чтобы добиться этого, у меня есть такой код:Выберите проекцию в Entity Framework и проведите фильтр

var result = await Context.People 
       .Select(p => new PersonWithSalutation 
       { 
        FirstName = p.FirstName, 
        LastName = p.FirstName, 
        FullSalutation = p.FirstName + " " + p.LastName 
       }) 
       .Where(p => p.FullSalutation.Length < maxLength) 
       .OrderBy(p => p.FullSalutation) 
       .Take(maxResults) 
       .ToListAsync(); 

Запрос выглядит следующим образом:

SELECT TOP (10) 
    [Project1].[C1] AS [C1], 
    [Project1].[Name] AS [Name], 
    [Project1].[Id] AS [Id], 
    [Project1].[C2] AS [C2] 
    FROM (SELECT 
     [Extent1].[Id] AS [Id], 
     [Extent1].[Name] AS [Name], 
     1 AS [C1], 
     ...calculated stuff... AS [C2] 
     FROM [dbo].[People] AS [Extent1] 
     WHERE ...exactly the same stuff... <= @p__linq__3 
    ) AS [Project1] 
    ORDER BY [Project1].[C2] ASC 

Это делает трюк и генерирует один запрос к базе данных. Проблема заключается в вычисленной проекции, поскольку она появляется дважды в результате запроса: один раз в SELECT, а затем в предложении WHERE. Этот пример упрощен, но в моем реальном случае я делаю тяжелые математические операции, которые я бы предпочли рассчитать только один раз. Как вы можете видеть выше, C2 повторно используется в предложении order. Я хотел бы сделать то же самое с предложением WHERE, которое, как я полагаю, связано с еще одним подзапросом). Как я могу это достичь?

+0

Я предположил бы, SQL Server, чтобы понять, что расчет одинакова и не делать он дважды в этом случае. У вас есть основания полагать, что это так? – John

ответ

1

Поскольку здание запроса является очень непредсказуемым процессом, вы можете потратить много времени на поиск желаемого linq. Вот почему я предлагаю другой подход. Добавьте FullSalutation к вашему POCO классу, и он будет рассчитан (Computed) с помощью БД при замене FirstName или LastName. В этом случае расчеты будут выполняться, когда действительно необходимо, без повторов, как вы хотели.

public class POCO 
{ 
    public int ID { get; set; } 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 

    [DatabaseGenerated(DatabaseGeneratedOption.Computed)] 
    public string FullSalutation { get; private set; } 
} 

Затем добавить новую миграцию:

public override void Up() 
{ 
    //AddColumn("dbo.People", "FullSalutation", x => x.String()); 
    Sql("ALTER TABLE dbo.People ADD FullSalutation AS FirstName + ' ' + LastName"); 
} 
public override void Down() 
{ 
    DropColumn("dbo.People", "FullSalutation"); 
} 

Наконец ваш запрос будет выглядеть следующим образом:

var result = await Context.People 
      .Where(p => p.FullSalutation.Length < maxLength) 
      .Select(p => new PersonWithSalutation 
      { 
        FirstName = p.FirstName, 
        LastName = p.LastName, 
        FullSalutation = p.FullSalutation 
      }).OrderBy(p => p.FullSalutation).Take(maxResults).ToListAsync();