2017-03-01 4 views
0

Ich benutze EF Core in einem asp.net MVC Core 1.1.0 Projekt und haben eine ziemlich komplexe Abfrage.EF Core mehrere Links Joins

_context 
.Profiles 
.Include(p => p.Blog) 
.ThenInclude(b => b.Network) 
.Include(p => p.Blog) 
.ThenInclude(i => i.AgeDistributions) 
    .ThenInclude(i => i.AgeRange) 
.Include(p => p.Blog) 
.ThenInclude(b => b.GenderDistributions) 
.Include(p => p.Instagram) 
.ThenInclude(i => i.Network) 
.Include(p => p.Instagram) 
.ThenInclude(i => i.AgeDistributions) 
    .ThenInclude(i => i.AgeRange) 
.Include(p => p.Instagram) 
.ThenInclude(b => b.GenderDistributions) 
.Include(p => p.Youtube) 
.ThenInclude(y => y.Network) 
.Include(p => p.Youtube) 
.ThenInclude(i => i.AgeDistributions) 
    .ThenInclude(i => i.AgeRange) 
.Include(p => p.Youtube) 
.ThenInclude(b => b.GenderDistributions) 
.Include(p => p.Snapchat) 
.ThenInclude(s => s.Network) 
.Include(p => p.Musically) 
.Include(p => p.ProfileCategories) 
.ThenInclude(pc => pc.Category) 
.Include(p => p.Tags) 
.ThenInclude(tag => tag.Tag) 
.Where(p => !p.Deleted); 

Jede soziale Plattform kann jede Art von Statistik haben. Zum Beispiel werden AgeDistributions mit einer Basisklasse modelliert, die eine PlatformId hat und jede abgeleitete {Platform}AgeDistribution spezifiziert die Navigationseigenschaft, um Fremdschlüssel richtig einzurichten.

public class AgeInterval { 
    public int Id { get; set; } 
    // At most five length. -18, 18-24, ..., 65- 
    public string Interval { get; set; } 
} 

public class PlatformAgeStatistics { 
    public int PlatformId { get; set; } 
    public int IntervalId { get; set; } 
    public AgeInterval Interval { get; set; } 
    public decimal Distribution { get; set; } 
} 

public class InstagramAgeStatistics : PlatformAgeStatistics { 
    [ForeignKey("PlatformId")] 
    public Instagram Platform { get; set; } // 
} 

Above Abfrage manchmal sehr lange Zeit (db Ausführungs-Timeout nach 30 Sekunden) und Inspektion der SQL ich denken entweder ich habe eine Modellierung Problem, dass EF nicht korrekt ermitteln oder EF ist nur suboptimal SQL zu generieren. Die Ergebnismenge wird mithilfe von skip und take paginiert und das Abrufen von zehn Datensätzen dauert zeitaufwendig.

Dies ist die erste SQL, die

SELECT -- Emitted 
FROM [Profiles] AS [p] 
LEFT JOIN [BlogChannels] AS [b] ON [b].[ProfileId] = [p].[Id] 
LEFT JOIN [InstagramChannels] AS [i] ON [i].[ProfileId] = [p].[Id] 
LEFT JOIN [YoutubeChannels] AS [y] ON [y].[ProfileId] = [p].[Id] 
LEFT JOIN [BlogChannels] AS [b2] ON [b2].[ProfileId] = [p].[Id] 
LEFT JOIN [InstagramChannels] AS [i2] ON [i2].[ProfileId] = [p].[Id] 
LEFT JOIN [YoutubeChannels] AS [y2] ON [y2].[ProfileId] = [p].[Id] 
LEFT JOIN [BlogChannels] AS [b4] ON [b4].[ProfileId] = [p].[Id] 
LEFT JOIN [Networks] AS [n] ON [b4].[NetworkId] = [n].[Id] 
LEFT JOIN [InstagramChannels] AS [i4] ON [i4].[ProfileId] = [p].[Id] 
LEFT JOIN [Networks] AS [n0] ON [i4].[NetworkId] = [n0].[Id] 
LEFT JOIN [YoutubeChannels] AS [y4] ON [y4].[ProfileId] = [p].[Id] 
LEFT JOIN [Networks] AS [n1] ON [y4].[NetworkId] = [n1].[Id] 
LEFT JOIN [SnapchatChannels] AS [s] ON [s].[ProfileId] = [p].[Id] 
LEFT JOIN [Networks] AS [n2] ON [s].[NetworkId] = [n2].[Id] 
LEFT JOIN [MusicallyChannels] AS [m] ON [m].[ProfileId] = [p].[Id] 
WHERE [p].[Deleted] = 0 
ORDER BY [p].[FullName], [p].[Id], [b].[Id], [i].[Id], [y].[Id], [b2].[Id], [i2].[Id], [y2].[Id] 
OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY 

Dann mehrere Abfragen folgt ausgeführt wird, die nicht ganz sieht rechts

SELECT [y3].[Gender], [y3].[ChannelId], [y3].[Distribution] 
FROM [YoutubeGenderDistribution] AS [y3] 
INNER JOIN (
    SELECT DISTINCT [t7].* 
    FROM (
     SELECT [p].[FullName], [p].[Id], [b].[Id] AS [Id0], [i].[Id] AS [Id1], [y].[Id] AS [Id2], [b2].[Id] AS [Id3], [i2].[Id] AS [Id4], [y2].[Id] AS [Id5] 
     FROM [Profiles] AS [p] 
     LEFT JOIN [BlogChannels] AS [b] ON [b].[ProfileId] = [p].[Id] 
     LEFT JOIN [InstagramChannels] AS [i] ON [i].[ProfileId] = [p].[Id] 
     LEFT JOIN [YoutubeChannels] AS [y] ON [y].[ProfileId] = [p].[Id] 
     LEFT JOIN [BlogChannels] AS [b2] ON [b2].[ProfileId] = [p].[Id] 
     LEFT JOIN [InstagramChannels] AS [i2] ON [i2].[ProfileId] = [p].[Id] 
     LEFT JOIN [YoutubeChannels] AS [y2] ON [y2].[ProfileId] = [p].[Id] 
     WHERE [p].[Deleted] = 0 
     ORDER BY [p].[FullName], [p].[Id], [b].[Id], [i].[Id], [y].[Id], [b2].[Id], [i2].[Id], [y2].[Id] 
     OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY 
    ) AS [t7] 
) AS [y20] ON [y3].[ChannelId] = [y20].[Id5] 
ORDER BY [y20].[FullName], [y20].[Id], [y20].[Id0], [y20].[Id1], [y20].[Id2], [y20].[Id3], [y20].[Id4], [y20].[Id5] 

eine andere, die "schaut" richtiger

SELECT [b0].[AgeRangeId], [b0].[ChannelId], [b0].[Distribution], [a].[Id], [a].[Range] 
FROM [BlogAgeDistribution] AS [b0] 
INNER JOIN (
    SELECT DISTINCT [t2].* 
    FROM (
     SELECT [p].[FullName], [p].[Id], [b].[Id] AS [Id0] 
     FROM [Profiles] AS [p] 
     LEFT JOIN [BlogChannels] AS [b] ON [b].[ProfileId] = [p].[Id] 
     WHERE [p].[Deleted] = 0 
     ORDER BY [p].[FullName], [p].[Id], [b].[Id] 
     OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY 
    ) AS [t2] 
) AS [b1] ON [b0].[ChannelId] = [b1].[Id0] 
LEFT JOIN [AgeRanges] AS [a] ON [b0].[AgeRangeId] = [a].[Id] 
ORDER BY [b1].[FullName], [b1].[Id], [b1].[Id0] 

Eine Idee, warum EF alle anderen Plattformen bei der Anforderung der Statistiken für zB verbindet Instagram.

Danke!

Edit:

Interessanterweise ist die erste Abfrage für Age ein mit allen drei

SELECT [y0].[AgeRangeId], [y0].[ChannelId], [y0].[Distribution], [a1].[Id], [a1].[Range] 
FROM [YoutubeAgeDistribution] AS [y0] 
INNER JOIN (
    SELECT DISTINCT [t4].* 
    FROM (
     SELECT [p].[FullName], [p].[Id], [b].[Id] AS [Id0], [i].[Id] AS [Id1], [y].[Id] AS [Id2] 
     FROM [Profiles] AS [p] 
     LEFT JOIN [BlogChannels] AS [b] ON [b].[ProfileId] = [p].[Id] 
     LEFT JOIN [InstagramChannels] AS [i] ON [i].[ProfileId] = [p].[Id] 
     LEFT JOIN [YoutubeChannels] AS [y] ON [y].[ProfileId] = [p].[Id] 
     WHERE [p].[Deleted] = 0 
     ORDER BY [p].[FullName], [p].[Id], [b].[Id], [i].[Id], [y].[Id] 
     OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY 
    ) AS [t4] 
) AS [y1] ON [y0].[ChannelId] = [y1].[Id2] 
LEFT JOIN [AgeRanges] AS [a1] ON [y0].[AgeRangeId] = [a1].[Id] 
ORDER BY [y1].[FullName], [y1].[Id], [y1].[Id0], [y1].[Id1], [y1].[Id2] 

Antwort

0

Der Grund EF Kern alle Plattformen trotz Sie abfragt beitreten erzeugt sie wollen nur eine bestimmte Plattform abfragen würde liegt daran, wie die Abfrage codiert ist. Sie haben alle im selben IQueryable kombiniert. Nutzen Sie den Vorteil, dass Sie Ihr IQueryable über mehrere Schritte in C# erstellen, bevor Sie das IQueryable ausführen.

var query = _context 
.Profiles.Where(p => searching.Contains(p.Name) && !p.Deleted) 

if(searching.Contains("Blog")) 
{ 
    query.Include(p => p.Blog) 
    .ThenInclude(b => b.Network) 
    .Include(p => p.Blog) 
    .ThenInclude(i => i.AgeDistributions) 
    .ThenInclude(i => i.AgeRange) 
    .Include(p => p.Blog) 
    .ThenInclude(b => b.GenderDistributions) 
} 

if(searching.Contains("Instagram")) 
{ 
.Include(p => p.Instagram) 
    .ThenInclude(i => i.Network) 
.Include(p => p.Instagram) 
    .ThenInclude(i => i.AgeDistributions) 
    .ThenInclude(i => i.AgeRange) 
.Include(p => p.Instagram) 
    .ThenInclude(b => b.GenderDistributions) 
} 

... 

var results = query.ToList(); 

Das letzte, an das man sich erinnern sollte, ist so früh wie möglich zu filtern. Deshalb habe ich ganz am Anfang ein "searching.Contains (p.Name)" gesetzt. Je kleiner der Speicherbedarf ist, den Ihre Abfrage ausführen muss. Je schneller es ausgeführt werden soll.

Die letzte Anmerkung, die ich hinzufügen kann, ist, dass EF Core noch ziemlich neu ist und nicht alles vollständig innerhalb der Datenbank ausgeführt wird. In einigen Fällen erstellt es eine Reihe von Abfragen, die unabhängig voneinander ausgeführt werden, und kombiniert sie dann zu einem endgültigen Ergebnis, das im aufrufenden Clientkontext festgelegt wird.