【问题标题】:Select a projection in Entity Framework and filter by it在实体框架中选择一个投影并按它过滤
【发布时间】:2016-04-09 22:23:20
【问题描述】:

简化示例:我有一个带有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 子句做同样的事情,我认为这将涉及另一个子查询)。我将如何实现这一目标?

【问题讨论】:

  • 我会假设 SQL Server 意识到计算是相同的,在这种情况下不会执行两次。你有理由相信它吗?

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


【解决方案1】:

由于查询的构建是一个非常不可预测的过程,您可能会花费大量时间来寻找所需的linq。这就是为什么,我提供另一种方法。将FullSalutation 属性添加到您的POCO 类中,当FirstNameLastName 更改时,它将通过DB 计算(Computed)。在这种情况下,将在真正需要时执行计算,无需重复,如您所愿。

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();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多