EF 4.1:为什么将常量转换为变量会导致额外的子查询?

Dan*_*zzi 4 c# linq-to-entities entity-framework code-first entity-framework-4

今天我发现实体框架正在向它生成的 SQL 添加一个不必要的子查询。我开始挖掘我的代码,试图缩小它的来源。A(很长)一段时间后,我指出了导致它的原因。但是现在我比开始时更困惑,因为我不知道为什么会这样。

基本上我发现的是,在某些情况下,简单地将常量转换为变量可以改变实体框架生成的 SQL。我已将所有内容缩小到最低限度,并将其打包在一个小型控制台应用程序中:

using System;
using System.Data.Entity;
using System.Linq;

class Program
{
    private static readonly BlogContext _db = new BlogContext();

    static void Main(string[] args)
    {
        const string email = "foo@bar.com";

        var comments = from c in _db.Comments
                       where c.Email == email
                       select c;

        var result = (from p in _db.Posts
                      join c in comments on p.PostId equals c.PostId
                      orderby p.Title
                      select new { p.Title, c.Content });

        Console.WriteLine(result);
    }
}

public class BlogContext : DbContext
{
    public DbSet<Post> Posts { get; set; }
    public DbSet<Comment> Comments { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
}

public class Comment
{
    public int CommentId { get; set; }
    public int PostId { get; set; }
    public string Email { get; set; }
    public string Content { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这显示了以下输出,这是完美的:

SELECT
[Extent1].[PostId] AS [PostId],
[Extent1].[Title] AS [Title],
[Extent2].[Content] AS [Content]
FROM  [dbo].[Posts] AS [Extent1]
INNER JOIN [dbo].[Comments] AS [Extent2] ON [Extent1].[PostId] = [Extent2].[PostId]
WHERE N'foo@bar.com' = [Extent2].[Email]
ORDER BY [Extent1].[Title] ASC
Run Code Online (Sandbox Code Playgroud)

现在,如果我创建email一个变量:

/*const*/ string email = "foo@bar.com";
Run Code Online (Sandbox Code Playgroud)

输出发生了根本性的变化:

SELECT
[Project1].[PostId] AS [PostId],
[Project1].[Title] AS [Title],
[Project1].[Content] AS [Content]
FROM ( SELECT
        [Extent1].[PostId] AS [PostId],
        [Extent1].[Title] AS [Title],
        [Extent2].[Content] AS [Content]
        FROM  [dbo].[Posts] AS [Extent1]
        INNER JOIN [dbo].[Comments] AS [Extent2] ON [Extent1].[PostId] = [Extent2].[PostId]
        WHERE [Extent2].[Email] = @p__linq__0
)  AS [Project1]
ORDER BY [Project1].[Title] ASC
Run Code Online (Sandbox Code Playgroud)

附带说明一下,LINQ to SQL 似乎没有这样做。我知道忽略这一点可能没问题,因为两个命令都返回相同的数据。但我非常好奇为什么会发生这种情况。直到今天,我一直有(也许是错误的?)印象,即只要值保持不变(在本例中是这样),将常量转换为变量总是安全的。所以我要问...

为什么一个看似微不足道的变化会导致生成的 SQL 产生如此大的差异?

更新:

明确地说,我的问题不是关于email在第一个查询中作为硬编码值和在第二个查询中作为变量的价值(这在世界上都是有意义的)。我的问题是关于为什么变量版本会导致额外的子查询。

谢谢!

Iva*_*nov 5

答案很简单。您的 LINQ 查询是用表达式树表示的。const 变量与非 const 变量的区别在于ConstantExpressionParameterExpression

当您使用 const 时,您的 LINQ 查询将ConstExpression用于此变量,而当您使用非 const 时,它将使用ParameterExpressionEF 运行时以不同方式解释的变量。

常量实际上意味着该值永远不会改变并且该值可以被内联到查询中。

  • @Euphoric:在问题(之后)被编辑之前,很难对一个看起来正确的答案投反对票 (4认同)

Wil*_*l A 1

这实际上是 SQL 中的一个很大的区别吗?内部查询与原始查询相同,外部查询只是内部查询的包装,不会更改结果集。

除非这会引起问题,否则我个人不会担心。两种查询风格的查询计划是否不同?我的猜测是它们是相同的。