2010-02-28 16 views
6

Betrachten Sie die folgende vereinfachte Objekte und Beziehungen:Wie gruppieren mehrere Elemente mit GroupBy()?

public class Job{ 
    int JobId; 
    String name; 
    String status; 
    Discipline disc; 
    WorkCategory workCat; 
} 

public class Discipline { 
    int DisciplineId; 
    String DisciplineName; 
} 

public class Workcategory{ 
    int WorkCategoryId; 
    String WorkCategoryName; 
} 

public Class Grouping{ 
    Discipline parent; 
    List<Workcategory> children; 
} 

oben genannten Modelle eine Beziehung, die a mit einem JobDiscipline und ein WorkCategory zugeordnet ist. Die Workcategory ist immer ein Kind eines bestimmten Elternteils Discipline (Eins-zu-viele-Beziehung). Wir können davon ausgehen, dass die Beziehung der zugewiesenen Disziplin und Workcategory immer gültig ist.

Das Problem, mit dem ich konfrontiert bin, ist, wenn ich versuche, ein Grouping Ergebnisobjekt basierend auf Filtern zu erstellen, die auf Jobs angewendet werden. Ich bin mir nicht sicher, ob dies möglich ist oder ob der Ansatz, den ich einstelle, sogar richtig ist. Die genaue Frage selbst ist mir nicht klar, jedoch definiert das Obige die Problemstellung.

  1. Kann das Design verbessert werden?
  2. Wie kann ich Jobs nach Disziplin und Workcategory gruppieren?
  3. Benötige ich sogar die Klasse Grouping?

Ich habe Folgendes versucht (dies ist mein erster Versuch mit Linq), aber zu keinem Erfolg, da mein Verständnis nicht vollständig genug ist. Die andere Alternative besteht darin, zuerst die Discipline Gruppe zu erhalten und durch die ursprüngliche Gruppierung zu gehen, wobei die zugehörige Workcategory abgerufen wird.

var grouping = repository.GetJobsWithActiveStatus() 
      .GroupBy(x => new { 
            x.Discipline.DisciplineID, 
            x.Discipline.DisciplineName, 
            x.Category.WorkCategoryID, 
            x.Category.WorkCategoryName 
           }) 
      .Select(g => new Grouping{ 
           Discipline = new Discipline{ 
                DisciplineID = g.Key.DisciplineID, 
                Name = g.Key.DisciplineName 
                }, 
           Categories = ?? // this is where I am lost 
          }}); 

Edit: Danach gebucht hat, wird mir klar, dass die inital GroupBy-Parameter in einer Gruppe von Punkt führen, während ich für die Gruppe-SubGroup Ergebnis suchen.

Edit 2: Um ein wenig weiter zu klären, ich will nicht die zugehörigen Jobs als Teil des Ergebnisses, sondern die Disziplin-Workcategory Gruppierung - also den Grund für die Grouping Klasse


Initial Lösung auf Basis von @Obalix

Edit 7-Mar-2010:

Diese Lösung funktioniert nicht - Die GroupBy für das Objekt Discipline ergibt eine eindeutige Gruppierung für jedes Objekt. Ich denke, das liegt daran, dass es ein Referenztyp ist. Hab ich recht? Ich akzeptierte dies zunächst als Antwort, aber nach einigen Kopfkratzern erkannte ich, dass meine gefälschten Daten selbst fehlerhaft waren. Die ersten Fragen bleiben weiterhin beantwortet.

Antwort

2

Here is a description wie Sie einen hierarchischen Gruppierungsmechanismus implementieren können.

Eine generische Art und Weise dies mit LINQ zu tun (wie in dem Link dargestellt):

var list = new List<Job>(); 

var groupedList = list.GroupBy(x => x.disc) 
    .Select(g => new { 
     Key = g.Key, 
     Count = g.Count(), 
     WorkCategoryGroups = g.GroupBy(x => x.workCat) 
    }); 

jedoch auch der Link auf eine alternative Art und Weise beschreibt, die Sie folgendes tun können:

var groupedList = list.GroupByMany(x => x.disc, x => x.workCat); 

Edit: Ahmads Kommentar hier Nach ist die stark typisierte Version:

var groupedList = list.GroupBy(x => x.disc) 
    .Select(g => new Grouping { 
     parent = g.Key, 
     children = g.GroupBy(x => x.workCat).ToList() 
    }); 
+0

Ich habe mir den Link angesehen und das Lesen der Kommentare hat mich zu einem anderen Beitrag geführt, also schauen wir uns das weiter an. Ich bin jedoch auf der Suche nach einem stark typisierten Ergebnis (Gruppierung) – Ahmad

+0

Dann ersetzen Sie einfach die 'neue {}' durch 'neue StrongType {}'. Wenn Sie "Key", "Count" und "WorkCategoryGroups" verwenden, verwenden Sie Ihre benutzerdefinierten Mitglieder. Der erweiterte Ansatz, der durch den Link beschrieben wird, ist stark typisiert. – AxelEckenberger

+0

FYI - die in der oben genannten Antwort geschrieben ist großartig - es gibt eine Follow-up-Post in Bezug auf Dynamic GroupByMany auch wenn jemand interessiert ist http://blogs.msdn.com/mitsu/archive/2008/02/07/linq -groupbymany-dynamically.aspx – Ahmad

2

würde ich Linq-Syntax verwenden:

var jobsByDiscipline = 
    from j in repository.GetJobsWithActiveStatus() 
    group j by j.Discipline.DisciplineID into g 
    select new { 
     DisciplineID = g.Key, 
     Jobs = g.ToList() 
    }; 

var jobsByCategory = 
    from j in repository.GetJobsWithActiveStatus() 
    group j by j.Workcategory.WorkcategoryID into g 
    select new { 
     WorkcategoryID = g.Key, 
     Jobs = g.ToList() 
    }; 

ich, dass leichter finden zu lesen, als viele gekettet Methoden mit Lambda-Funktion Parameter.

Sie können Ihre Gruppierungen von diesem erhalten:

var disciplineAndCategory = 
    from j in repository.GetJobsWithActiveStatus() 
    group j by j.Discipline.DisciplineID into g 
    let categories = 
     from j2 in g 
     select j2.Workcategory.WorkcategoryID 
    select new { 
     DisciplineID = g.Key, 
     Jobs = categories.Distinct() // each category id once 
    }; 
+1

+1 Vereinbart, diese Syntax ist ** viel ** einfacher zu lesen, wenn Sie Gruppierung. –

+0

@keith, danke für den Vorschlag, mein erster Versuch, es zu verwenden schien - aber es wurde nicht getestet, aber ich bevorzuge die verketteten Methoden Ansatz - persönliche Präferenz denke ich :) – Ahmad

0

Hier ist eine andere Art und Weise, die nicht die Gruppierung Klasse erfordert.

ILookup<Discipline, Workcategory> result = repository 
    .GetJobsWithActiveStatus() 
    .Select(job => new {disc = job.disc, workCat = job.workCat}) 
    .Distinct() 
    .ToLookup(x => x.disc, x => x.workCat); 
+0

Nach dem Testen dieser, ergibt es ein ähnliches Ergebnis zu Obalix Antworten. Eine Gruppierung für jede Disziplin und Arbeitskategorie – Ahmad

0

Dies ist die Lösung, die für mich funktioniert.

Zuerst musste ich alle Disziplingruppen erstellen und dann die zugehörigen Workcategory-Listen erstellen, in denen disciplineID gleich ist.

var result = liveJobs 
.GroupBy(x => new { 
       x.disc.DisciplineID, 
       x.disc.DisciplineName 
        }) 
.Select(g => new Grouping() 
       { 
       parent = new Discipline(g.Key.DisciplineID,g.Key.DisciplineName), 
       children = g.Where(job=>job.disc.DisciplineID == g.Key.DisciplineID) // filter the current job discipline to where the group key disciplines are equal 
       .Select(j=>j.workCat) 
       .ToList() 
       });