Reputation: 411
Simplified example: I have a table with FirstName
, LastName
. I am interested in retrieving all the people whose full salutation is not longer by N, sorted by the length. To achieve that, I have code like this:
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();
The query looks like this:
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
This does the trick and generates a single query to the database. The problem is the calculated projection since it appears twice in the resulting query: once in the SELECT
and then in an WHERE
clause. This example is simplified, but in my real case I am doing heavy mathematical operations that I would prefer to be calculated only once. As you can see above, the C2 is reused in the order clause. I would like to do the same with the WHERE
clause, which I assume would involve yet another subquery). How would I achieve this?
Upvotes: 0
Views: 771
Reputation: 13488
Because query's building is very unpredictable process, you can spend a lot of time by looking for desired linq
. That is why, I offer another approach. Add FullSalutation
property to your POCO
class and it will be calculated(Computed
) by means of DB when FirstName
or LastName
is changed. At this case calculations will be performed when it is really necessary, without repeats, as you desired.
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; }
}
Then add new migration:
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");
}
Finally your query will look like this:
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();
Upvotes: 1