Ich habe wirklich schwer Zeit, eine meiner Entity Framework
generierten Abfragen in meiner Anwendung zu tunen. Es ist sehr einfache Abfrage, aber aus irgendeinem Grund verwendet EF
mehrere innere Unterabfragen, die schrecklich in DB
anstelle von Joins durchzuführen scheinen.LINQ und Entity Framework - Unterabfragen zu vermeiden
Hier ist meine LINQ-Code:
Projects.Select(proj => new ProjectViewModel()
{
Name = proj.Name,
Id = proj.Id,
Total = proj.Subvalue.Where(subv =>
subv.Created >= startDate
&& subv.Created <= endDate
&&
(subv.StatusId == 1 ||
subv.StatusId == 2))
.Select(c => c.SubValueSum)
.DefaultIfEmpty()
.Sum()
})
.OrderByDescending(c => c.Total)
.Take(10);
EF erzeugt wirklich komplexe Abfrage mit mehreren Unterabfragen, die schreckliche Abfrageleistung wie diese hat:
SELECT TOP (10)
[Project3].[Id] AS [Id],
[Project3].[Name] AS [Name],
[Project3].[C1] AS [C1]
FROM (SELECT
[Project2].[Id] AS [Id],
[Project2].[Name] AS [Name],
[Project2].[C1] AS [C1]
FROM (SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
(SELECT
SUM([Join1].[A1]) AS [A1]
FROM (SELECT
CASE WHEN ([Project1].[C1] IS NULL) THEN cast(0 as decimal(18)) ELSE [Project1].[SubValueSum] END AS [A1]
FROM (SELECT 1 AS X) AS [SingleRowTable1]
LEFT OUTER JOIN (SELECT
[Extent2].[SubValueSum] AS [SubValueSum],
cast(1 as tinyint) AS [C1]
FROM [dbo].[Subvalue] AS [Extent2]
WHERE ([Extent1].[Id] = [Extent2].[Id]) AND ([Extent2].[Created] >= '2015-08-01') AND ([Extent2].[Created] <= '2015-10-01') AND ([Extent2].[StatusId] IN (1,2))) AS [Project1] ON 1 = 1
) AS [Join1]) AS [C1]
FROM [dbo].[Project] AS [Extent1]
WHERE ([Extent1].[ProjectCountryId] = 77) AND ([Extent1].[Active] = 1)
) AS [Project2]
) AS [Project3]
ORDER BY [Project3].[C1] DESC;
Die Ausführungszeit der Abfrage von EF erzeugt wird ~10 seconds
. Aber wenn ich die Abfrage von Hand wie folgt schreibe:
select
TOP (10)
Proj.Id,
Proj.Name,
SUM(Subv.SubValueSum) AS Total
from
SubValue as Subv
left join
Project as Proj on Proj.Id = Subv.ProjectId
where
Subv.Created > '2015-08-01' AND Subv.Created <= '2015-10-01' AND Subv.StatusId IN (1,2)
group by
Proj.Id,
Proj.Name
order by
Total DESC
Die Ausführungszeit ist fast sofort; unter 30ms
.
Das Problem liegt klar in meiner Fähigkeit, gute EF
Abfragen mit LINQ zu schreiben, aber egal was ich versuche zu tun (Linqpad
zum Testen verwenden) Ich kann einfach nicht ähnlich performant Abfrage mit LINQ\EF
schreiben, wie ich mit der Hand schreiben kann. Ich habe die Tabelle SubValue Tabelle und Projekt Tabelle, aber das Ergebnis ist meist das gleiche: mehrere ineffiziente verschachtelte Unterabfragen anstelle von einem einzigen Join die Arbeit.
Wie kann ich eine Abfrage schreiben, die die oben gezeigte Hand SQL
imitiert? Wie kann ich die tatsächliche Abfrage steuern, die von EF
generiert wird? Und am wichtigsten: Wie kann ich Linq2SQL
und Entity Framework
Joins
verwenden, wenn ich anstelle von verschachtelten Unterabfragen will.
Mit ** ** LINQ Sie via Modem eine 'DataContext' verwenden. Sehen Sie sich die Eigenschaft 'DataContext.LoadOptions' [hier] an (https: // msdn.microsoft.com/en-us/library/system.data.linq.datacontext.loadoptions%28v=vs.110%29.aspx) .. –
set context.ObjectTrackingEnabled = false, Sie werden eine große Verbesserung erhalten. –
@JanUnld danke für den Vorschlag, aber ich bin mir nicht sicher, wie es bei diesem spezifischen Problem helfen würde? Kannst du es näher erklären? – veturi