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在第一个查询中作为硬编码值和在第二个查询中作为变量的价值(这在世界上都是有意义的)。我的问题是关于为什么变量版本会导致额外的子查询。
谢谢!
答案很简单。您的 LINQ 查询是用表达式树表示的。const 变量与非 const 变量的区别在于ConstantExpression和 ParameterExpression。
当您使用 const 时,您的 LINQ 查询将ConstExpression用于此变量,而当您使用非 const 时,它将使用ParameterExpressionEF 运行时以不同方式解释的变量。
常量实际上意味着该值永远不会改变并且该值可以被内联到查询中。
这实际上是 SQL 中的一个很大的区别吗?内部查询与原始查询相同,外部查询只是内部查询的包装,不会更改结果集。
除非这会引起问题,否则我个人不会担心。两种查询风格的查询计划是否不同?我的猜测是它们是相同的。
| 归档时间: |
|
| 查看次数: |
1139 次 |
| 最近记录: |