2011-01-15 6 views
0

Ich habe die folgende Abfrage:Linq2Sql: Abfrage - Unterabfrage-Optimierung

  IList<InfrStadium> stadiums = 
       (from sector in DbContext.sectors 
       where sector.Type=typeValue 
       select new InfrStadium(sector.TeamId) 
       ).ToList(); 

und InfrStadium Klassenkonstruktors:

private InfrStadium(int teamId) 
    { 
     IList<Sector> teamSectors = (from sector in DbContext.sectors 
            where sector.TeamId==teamId 
            select sector) 
            .ToList<>(); 
     ... work with data 
    } 

Aktuelle Implementierung durchführen 1 + n-Abfragen, wobei n - Anzahl der Datensätze abgerufen die 1. Mal.

Ich möchte das optimieren.

Und ein anderer würde ich lieben, wie dies mit ‚Gruppe‘ Operator in Art und Weise zu tun:

  IList<InfrStadium> stadiums = 
       (from sector in DbContext.sectors 
       group sector by sector.TeamId into team_sectors 
       select new InfrStadium(team_sectors.Key, team_sectors) 
       ).ToList(); 

mit entsprechenden Konstruktor:

private InfrStadium(int iTeamId, IEnumerable<InfrStadiumSector> eSectors) 
    { 
     IList<Sector> teamSectors = eSectors.ToList(); 

     ... work with data 
    } 

Aber versuchen Abfrage zu starten, verursacht den folgenden Fehler:

Expression of type 'System.Int32' cannot be used for constructor parameter of type 'System.Collections.Generic.IEnumerable`1[InfrStadiumSector]'

Frage 1:

Könnten Sie bitte erklären, was hier falsch ist, ich verstehe nicht, warum 'team_sectors' als 'System.Int32' angewendet wird?

ich Abfrage ein wenig zu ändern, habe versucht (mit IQueryeable IEnumerable ersetzen):

  IList<InfrStadium> stadiums = 
       (from sector in DbContext.sectors 
       group sector by sector.TeamId into team_sectors 
       select new InfrStadium(team_sectors.Key, team_sectors.AsQueryable()) 
       ).ToList(); 

mit entsprechenden Konstruktor:

private InfrStadium(int iTeamId, IQueryeable<InfrStadiumSector> eSectors) 
    { 
     IList<Sector> teamSectors = eSectors.ToList(); 

     ... work with data 
    } 

In diesem Fall habe ich erhielt eine andere, aber ähnliche Fehler:

Expression of type 'System.Int32' cannot be used for parameter of type 'System.Collections.Generic.IEnumerable 1[InfrStadiumSector]' of method 'System.Linq.IQueryable 1[InfrStadiumSector] AsQueryableInfrStadiumSector'

Frage 2:

Eigentlich die gleiche Frage: kann nicht verstehen, was hier vor sich geht ...

P. S. Ich habe eine andere Abfrage Idee zu optimieren (hier beschreiben: Linq2Sql: query optimisation) aber ich würde gerne eine Lösung mit 1 Anfrage an DB finden).

+0

Ich habe Ihren obigen Code implementiert und nicht den Fehler erhalten, den Sie erwähnen. Ich musste ein paar Syntaxfehler beheben, daher vermute ich, dass Sie Ihren Code nicht korrekt kopiert haben. Können Sie mir sagen, warum Sie in Ihrem Konstruktor arbeiten? Das ist schlechte Praxis. Ich denke, David B's Antwort ist eine gute. – Enigmativity

+0

Wahrscheinlich der Grund für das Problem auf meiner Seite ist, dass für den Zugriff auf die DB ich eigentlich Linq2sql, aber ein BLToolkit-Framework, das Zugriff auf IQueryable Schnittstelle bietet. Will versuchen, mit dem Framework-Besitzer zu sprechen. – Budda

+0

Enigmativität, ich mache Sachen im Konstruktor, weil ich mein Objekt nach der Konstruktion vollständig initialisiert haben möchte. Könnten Sie sagen, warum die Arbeit im Konstruktor schlecht ist? – Budda

Antwort

3

Zuerst ziehen Sie die Daten lokal und fügen Sie sie in eine Struktur ein, die Ihren Anforderungen entspricht.

ILookup<int, InfrStadiumSector> sectorLookup = 
(
    from sector in DbContext.sectors 
    where sector.Type == typeValue 
    select sector 
).ToLookup(sector => sector.TeamId); 

projizieren Dann wird jede Gruppierung in dieser Lookup in eine Instanz von InfrStadium (ohne auf die Datenbank zurück) ...

IList<InfrStadium> stadiums = sectorLookup 
    .Select(x => new InfrStadium(x.Key, x)) 
    .ToList(); 

Und das Projektion verwendet diesen Konstruktor.

private InfrStadium(int iTeamId, IEnumerable<InfrStadiumSector> eSectors) 
+0

In meinem ursprünglichen Beitrag habe ich nicht erwähnt, dass ich auch Daten nach aggregiertem Ausdruck ordnen möchte, etwa so: orderby team_sectors.Sum (ts => ts.Size) absteigend. Deshalb werde ich dies mit dem aktuellen Approach auf Kundenseite tun müssen. Glaube nicht, dass es wirklich einen großen Unterschied in der Leistung macht. – Budda

1

Ich kann nicht sicher sein, was ohne ein wenig Experimentieren vor sich geht. Sind Sie sich der Reihenfolge Ihrer Parameter im Konstruktor sicher? Wenn ja, könnte es ein Problem sein, den Ausdruck zu übersetzen. Sie sollten versuchen, die Abfrage materialisieren, bevor Sie versuchen, die InfrStadium Objekte zu erstellen. Ich werde mit Erweiterungsmethoden neu schreiben, da ich denke, dass es einfacher zu lesen ist.

var stadiums = DbContext.sectors 
         .ToLookup(s => s.TeamId) 
         .Select(g => new InfrStadium(g.Key, g)) 
         .ToList(); 
+0

Schließen, aber ... wenn die Gruppierung in der Datenbank durchgeführt wird, wird er n + 1 Abfragen sein, die den Inhalt jeder Gruppe aus der Datenbank abruft aufgrund des Unterschieds zwischen dem Gruppierungsergebnis "key and aggregates" von sql und der hierarchischen Gruppierung von linq Ergebnis. –

+0

@David - ah, daran hatte ich nicht gedacht. Normalerweise verwende ich die Gruppierung mit Aggregaten in einem anonymen Objekt. Ich denke, ich muss die ToList() in der Kette nach oben verschieben. – tvanfosson

+0

@David - an zweiter Stelle, dachte, dein ist besser, aber ich mag es alles zusammen angekettet. – tvanfosson