Сегодня я обнаружил, что Entity Framework добавляет ненужный подзапрос к генерируемому SQL. Я начал копаться в своем коде, пытаясь определить, откуда он мог взяться. (Долго), а позже я точно определил, что вызывает это. Но сейчас я еще больше запутался, чем когда начал, так как понятия не имею, почему это вызывает это.
По сути, я обнаружил, что в определенных сценариях простое преобразование константы в переменную может изменить SQL, который генерирует Entity Framework. Я сократил все до минимума и упаковал в маленькое консольное приложение:
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 = "[email protected]";
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; }
}
Это показывает следующий вывод, который идеален:
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'[email protected]' = [Extent2].[Email]
ORDER BY [Extent1].[Title] ASC
Теперь, если я сделаю email
переменной:
/*const*/ string email = "[email protected]";
Вывод кардинально меняется:
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
В качестве примечания, LINQ to SQL, похоже, этого не делает. Я знаю, что можно игнорировать это, так как обе команды возвращают одни и те же данные. Но мне очень интересно, почему так происходит. Вплоть до сегодняшнего дня у меня всегда было (возможно, ложное?) впечатление, что всегда безопасно превратить константу в переменную, при условии, что значение остается прежним (что в данном случае и есть). Поэтому я должен спросить...
Почему, казалось бы, незначительное изменение вызывает такую большую разницу в сгенерированном SQL?
Обновление:
Просто чтобы прояснить, мой вопрос не о том, что значение email
является жестко запрограммированным значением в первом запросе и переменной во втором (что имеет смысл). Мой вопрос о том, почему версия переменной приводит к дополнительному подзапросу.
Спасибо!