2016-06-30 9 views
3

Ich habe ein Viewmodel für die Statistik-Seite wie folgt erstellt:GroupBy komplexe Abfrage

public class StatsSeasonViewModel 
{ 
    public int player_id { get; set; } 
    public string player_name { get; set; } 
    public int games_played { get; set; } 
    public int total_first { get; set; } 
    public int total_second { get; set; } 
    public int total_third { get; set; } 
    public int total_wickets { get; set; } 
    public double avg_wickets { get; set; } 
    public int total_points { get; set; } 
    public double avg_points { get; set; } 
} 

ich eine komplexe LINQ-Anweisung haben, das Modell zu füllen. Ich empfinde dies einfacher sein könnte, aber ich weiß nicht, wie es zu tun:

const int first_place = 5; 
const int second_place = 3; 
const int third_place = 1; 

var model = 
from s in _db.Stats 
join p in _db.Players 
on s.player_id equals p.player_id 
where s.season_id == current_season 
select new StatsSeasonViewModel 
{ 
    player_id = p.player_id, 
    player_name = p.name, 
    games_played = (from st1 in _db.Stats 
        where st1.player_id == s.player_id 
        select st1).Count(), 
    total_first = (from st2 in _db.Stats 
        where st2.player_id == s.player_id && st2.place == 1 
        select st2).Count(), 
    total_second = (from st3 in _db.Stats 
        where st3.player_id == s.player_id && st3.place == 2 
        select st3).Count(), 
    total_third = (from st4 in _db.Stats 
        where st4.player_id == s.player_id && st4.place == 3 
        select st4).Count(), 
    total_wickets = (from st5 in _db.Stats 
         where st5.player_id == s.player_id 
         select st5.wickets).Sum(), 
    avg_wickets = (from st5 in _db.Stats 
        where st5.player_id == s.player_id 
        select st5.wickets).Sum()/
        (from st1 in _db.Stats 
        where st1.player_id == s.player_id 
        select st1).Count(), 
    total_points = (from st5 in _db.Stats 
        where st5.player_id == s.player_id 
        select st5.wickets).Sum() + 
        (
         (from st2 in _db.Stats 
          where st2.player_id == s.player_id && st2.place == 1 
          select st2).Count() 
        ) * first_place + 
        (
         (from st3 in _db.Stats 
          where st3.player_id == s.player_id && st3.place == 2 
          select st3).Count() 
        ) * second_place + 
        (
         (from st4 in _db.Stats 
          where st4.player_id == s.player_id && st4.place == 3 
          select st4).Count() 
        ) * third_place, 
    avg_points = (
        (from st5 in _db.Stats 
         where st5.player_id == s.player_id 
         select st5.wickets).Sum() + 
        (
         (from st2 in _db.Stats 
          where st2.player_id == s.player_id && st2.place == 1 
          select st2).Count() 
        ) * first_place + 
        (
         (from st3 in _db.Stats 
          where st3.player_id == s.player_id && st3.place == 2 
          select st3).Count() 
        ) * second_place + 
        (
         (from st4 in _db.Stats 
          where st4.player_id == s.player_id && st4.place == 3 
          select st4).Count() 
        ) * third_place 
       )/
       (from st1 in _db.Stats 
        where st1.player_id == s.player_id 
        select st1).Count() 
}; 

das größte Problem habe ich jetzt so ist, dass ich durch eine Gruppe tun müssen, auf diese Abfrage so, dass es nicht zeige Duplikate an. Aber wenn ich versuche, die Gruppe um hinzuzufügen, dann verliere ich den Rest der Abfrage nach dem SELECT. Wie kann ich eine Gruppe anhand der obigen Abfrage erstellen und die Ergebnisse erhalten, die ich brauche?

EDIT: FWIW ist hier die Ergebnisse erhalte ich: http://ecl.moyl.com/Home/Stats

Das zweite Problem ist natürlich die Komplexität der Abfrage selbst. Gibt es einen einfacheren Weg, dies zu tun?

+0

Einen Enumerable Bereich von 1 bis 3 dann nur filtern wie dieser . Wo (st => range.Contains (st.place) & st.player_id = s.player_id) dann müssen Sie nach Ort gruppieren. Aber es könnte nur sinnvoller sein, all dies in einer Ansicht in Ihrer Datenbank zu sehen, so dass Sie einfach von dort greifen können. Aber du bekommst einen Upvote für deine Mühe –

+0

Ha! Danke für die Verbesserung! Ich möchte nach Player_id gruppieren, nicht nach Ort. Gerade jetzt, was passiert ist, bekomme ich doppelte Einträge für alle Spieler. Ich habe versucht, Gruppen von s.player_id in statGroup zu addieren, aber dann bombardieren all die komplexen Sachen in der select-Anweisung, wenn ich versuche, das Modell zu bauen. Sie können mit der Erstellung einer Ansicht in der Datenbank recht haben - seltsamerweise habe ich das noch nie zuvor probiert! – xgrinderx

+1

Sie könnten die Ansicht nützlicher finden, denn wenn alles richtig indexiert ist, müssen Sie nicht so viel Arbeit erledigen, um diese Abfrage herauszuziehen. Und Sie können diese Ansicht in Entitätsframework ziehen –

Antwort

4

Es gibt ein paar Möglichkeiten, die Dinge zu vereinfachen, aber Ihre wichtigste Frage zu beantworten:

var search = from st2 in _db.Stats 
    where st2.player_id = s.player_id 
    group st2 by st2.player_id 

Sie über jede Gruppe laufen würden die IGrouping<TKey, TElement> bekommen die einzelnen Zählungen (ref) zu erhalten.

for (var playerGroup in search) 
{ 
    Console.WriteFormat("{0}: {1}\n", playerGroup.Key, playerGroup.Count()); 
} 

Ihr Code könnte ein bisschen besser lesbar sein, wenn Sie Ihre Zählungen/Summen etwas anders schreiben.

Beispiel dieses:

games_played = (from st1 in _db.Stats 
       where st1.player_id == s.player_id 
       select st1).Count(), 
total_wickets = (from st5 in _db.Stats 
        where st5.player_id == s.player_id 
        select st5.wickets).Sum() 

diese werden könnte:

var filter = st => st.player_id == s.player_id; // reuse this over and over 

games_played = _db.Stats.Where(filter).Count(), 
total_wickets = _db.Stats.Where(filter).Sum(st5 => st5.wickets) 

In der Tat, "haben Sie Ihren Kuchen und essen es auch", wird das gesamte Filter nicht erforderlich, wenn Sie Verwenden Sie die Gruppe nach Anweisung. Sie müssen ändern, wie Sie Ihr Modell erstellen, so dass Sie Ihre IGrouping<int,Stat> (vorausgesetzt, das ist, was die Typen sind) an den Konstruktor übergeben.Bei diesem Ansatz sieht Ihre allgemeine Abfrage wie folgt:

const int first_place = 5; 
const int second_place = 3; 
const int third_place = 1; 

var model = 
    from s in _db.Stats 
    join p in _db.Players 
    on s.player_id equals p.player_id 
    where s.season_id == current_season 
    group st by st.player_id into group 
    select new StatsSeasonViewModel(group) 

Ihr Jetzt ist StatsSeasonViewModel verantwortlich für die Statistik bevöl basierend auf den Werten in der Gruppe

public StatsSeasonViewModel(IGrouping<int,Stat> playerStats) 
{ 
    player_id = playerStats.Key; 
    games_played = playerStats.Count(); 
    total_wickets = playerStats.Sum(st=>st.wickets); 
    // .... 
} 
+0

Sie können die Filterung einiger der Statistiken vereinfachen, indem Sie sagen wo <= 3 –

+0

@ johnny5, es würde den Code vereinfachen, indem Sie es kürzer, aber nicht einfacher zu lesen, ich denke, Berin ist hier –

+1

Berin das ist genial. Ich bin total noob von diesem klassischen ASP-Hintergrund versucht, C#, MVC und LINQ mit dieser kleinen App zu lernen. Ich hatte das ursprünglich in der Frage selbst, aber ich denke, es wurde als unnötig abgeschwächt. Ich habe am Ende eine Ansicht verwendet, wie sie oben von johnny 5 vorgeschlagen wurde, aber ich denke, Ihre Antwort hat mich zumindest in die richtige Richtung gelenkt, um zu lernen, Gruppierung zu verwenden, während ich mich im Lernen fortbewegte. VIELEN DANK! – xgrinderx

1

Sie verwenden eine veraltete Methode zum Schreiben von Linq in MVC. Verwenden Sie die DbContext leicht die Spieler zu finden, indem ein Verfahren zu schaffen, der einen Spieler Id als Variable nimmt und verwenden Distinct unterschiedliche Werte zurück und vermeiden Duplikate:

var player = _db.Players.Where(p => p.player_id == Id); 

behandeln dann die Attribute von Lambda-Beitritt der Spieler mit dem Statistiken Tabelle:

var st1 in _db.Stats.Where(st1 => st1.player_id == player.player_id).Distinct().Count(); 

EDIT:

ich es ein wenig verändert, weil Sie den eindeutigen in dem zweiten Ausdruck wollen würde, da der erste wäre schon einen eindeutigen Wert finden