在Entity Framework中选择一个投影并按其过滤

时间:2016-04-09 22:23:21

标签: sql-server performance entity-framework linq-to-entities projection

简化示例:我的表格为FirstNameLastName。我有兴趣检索所有那些完全称呼不再为N的人,按长度排序。为此,我有这样的代码:

  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子句做同样的事情,我认为这将涉及另一个子查询。我怎么做到这一点?

1 个答案:

答案 0 :(得分:1)

由于查询的构建过程非常难以预测,因此您可以通过查找所需的linq来花费大量时间。这就是为什么,我提供另一种方法。将FullSalutation媒体资源添加到POCO课程中,当ComputedFirstName发生变化时,将通过数据库计算(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();