我正在测试使用EF
和Linq to Entities
来尝试提高我的应用程序性能。
我注意到了一些奇怪的东西(对我来说),我无法解释,也无法确定是否会产生可观的开销。
这是我的Linq:
var result = from n in query
orderby n.PersonId
select new
{
id = n.Id,
appointmentId = n.AppointmentId,
message = n.Message,
wasRead = n.Read,
canDismiss = (n.Appointment.Status != AppointmentStatus.Waiting),
date = n.IssueDateUtc
};
这是生成的SQL语句:
SELECT
[Project1].[Id] AS [Id],
[Project1].[AppointmentId] AS [AppointmentId],
[Project1].[Message] AS [Message],
[Project1].[Read] AS [Read],
[Project1].[C1] AS [C1],
[Project1].[IssueDateUtc] AS [IssueDateUtc]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Read] AS [Read],
[Extent1].[Message] AS [Message],
[Extent1].[IssueDateUtc] AS [IssueDateUtc],
[Extent1].[AppointmentId] AS [AppointmentId],
[Extent1].[PersonId] AS [PersonId],
CASE WHEN ( NOT ((1 = [Extent2].[Status]) AND ([Extent2].[Status] IS NOT NULL))) THEN cast(1 as bit) WHEN (1 = [Extent2].[Status]) THEN cast(0 as bit) END AS [C1]
FROM [dbo].[Notification] AS [Extent1]
LEFT OUTER JOIN [dbo].[Appointment] AS [Extent2] ON [Extent1].[AppointmentId] = [Extent2].[Id]
WHERE [Extent1].[PersonId] = @p__linq__0
) AS [Project1]
**ORDER BY [Project1].[PersonId] ASC**
我不明白为什么需要将结果在另一个投影(Project1
)中进行分组,而以下操作似乎可以正常工作:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Read] AS [Read],
[Extent1].[Message] AS [Message],
[Extent1].[IssueDateUtc] AS [IssueDateUtc],
[Extent1].[AppointmentId] AS [AppointmentId],
[Extent1].[PersonId] AS [PersonId],
CASE WHEN ( NOT ((1 = [Extent2].[Status]) AND ([Extent2].[Status] IS NOT NULL))) THEN cast(1 as bit) WHEN (1 = [Extent2].[Status]) THEN cast(0 as bit) END AS [C1]
FROM [dbo].[Notification] AS [Extent1]
LEFT OUTER JOIN [dbo].[Appointment] AS [Extent2] ON [Extent1].[AppointmentId] = [Extent2].[Id]
WHERE [Extent1].[PersonId] = @p__linq__0
**ORDER BY [Extent1].[PersonId] ASC**
我发现EF和LINQ生成的SQL存在相当多可疑代码,我开始怀疑是否直接编写原始SQL更好。问题是:生成的SQL中的额外代码是否值得担心?为什么需要那个投影?如评论中所述,可能是由于后续查询而导致的冗长。我重新编写了一条LINQ,只使用一个查询对象,结果仍然相同。
dbSet.Where(n => n.PersonId == id).Select(n => new
{
Id = n.Id,
AppointmentId = n.AppointmentId,
Message = n.Message,
Read = n.Read,
CanBeDismissed = (n.Appointment.Status != AppointmentStatus.Waiting),
IssueDate = n.IssueDateUtc
}).OrderBy(n => n.Id).ToList();
执行计划(对于两个sqls
相同)
编辑2
从简单的计数中得到了这个查询。
dbSet.Count(x => x.Id == 1 && x.Read == false);
SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(1) AS [A1]
FROM [dbo].[Notification] AS [Extent1]
WHERE ([Extent1].[PersonId] = 19) AND (0 = [Extent1].[Read])
) AS [GroupBy1]
期望结果:
SELECT
COUNT(1) AS [A1]
FROM [dbo].[Notification] AS [Extent1]
WHERE ([Extent1].[PersonId] = 19) AND (0 = [Extent1].[Read])
我无法理解所有这些包装器从哪里来以及为什么会出现。
query
是另一个 LINQ-to-SQL 查询,这就是你在投影中嵌套的内容。这正是你在使用 LINQ 做的事情。如果你想摆脱它,只需将OrderBy
和Select
添加到query
中即可。但如果使用后续的 LINQ 查询使你的代码更清晰,那就不要担心... 投影几乎没有额外的开销。如有疑问,请比较执行计划。但如果你看到性能问题,那肯定不是由于该项目引起的。 - Bernhard KoenigdbSet.Where()
中的where
到底是做什么的。 - Bernhard Koenigwhere
(n => n.PersonId == id
)。谢谢。 - victor