2010-02-28 10 views
6

considerare i seguenti oggetti e relazioni semplificate:Come raggruppare più elementi usando 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; 
} 

I modelli sopra un rapporto che un Job è associato con un Discipline e uno WorkCategory. Il Workcategory è sempre un figlio di un genitore specifico Discipline (relazione uno-a-molti). Possiamo supporre che la relazione tra la Disciplina e la categoria di lavoro assegnate sarà sempre valida.

Il problema che sto affrontando è quando sto cercando di creare un oggetto risultato Grouping in base ai filtri applicati a Jobs. Non sono sicuro che questo possa essere fatto o se l'approccio che sto prendendo sia anche corretto. L'esatta domanda in sé non mi è chiara, tuttavia quanto sopra definisce la dichiarazione del problema.

  1. Il design può essere migliorato?
  2. Come posso raggruppare i lavori in base a Disciplina e Workcategory?
  3. Ho anche bisogno della classe Grouping?

Ho provato quanto segue (questo è il mio primo tentativo di usare Linq), ma senza successo in quanto la mia comprensione non è abbastanza completa. L'altra alternativa è quella di ottenere prima il gruppo Discipline e il ciclo attraverso il raggruppamento originale raccogliendo il relativo Workcategory.

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: Dopo aver postato questo, mi sono reso conto che i parametri GroupBy inital risultato in un gruppo di voce mentre io sono alla ricerca di risultato del Gruppo-sottogruppo.

Edit 2: Per chiarire un po 'più, non voglio il processi associati come parte del risultato, ma il raggruppamento piuttosto la Disciplina-Workcategory - quindi la ragione per la classe Grouping


iniziale soluzione basata su @Obalix

Edit 7-mar-2010:

Questa soluzione non funziona - Il GroupBy sull'oggetto Disciplina produrrà un raggruppamento univoco per ciascun oggetto. Penso che questo sia dovuto al fatto che è un tipo di riferimento. Ho ragione? Inizialmente l'ho accettato come risposta, tuttavia dopo alcuni grattacapi capì che i miei dati falsi erano errati. Le domande iniziali rimangono ancora risposte.

var result = repository.GetJobsWithActiveStatus() 
     .GroupBy(x => x.disc) 
     .Select(g => new Grouping 
       { 
        Discipline = g.Key, 
        Catergories = g.GroupBy(x=>x.workCat) // <--- The Problem is here, grouping on a reference type 
           .Select(l=>l.Key) // needed to select the Key's only 
           .ToList() 
       }); 

risposta

2

Here is a description come è possibile implementare un meccanismo di raggruppamento gerarchico.

Un modo generico di fare questo utilizzando LINQ (come mostrato nel link) è:

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) 
    }); 

Tuttavia, il link descrive anche un modo alternativo che permette di effettuare le seguenti operazioni:

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

Edit: seguito il commento di Ahmad ecco la versione fortemente tipizzato:

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

Ho dato un'occhiata al link e leggendo i commenti mi ha portato ad un altro post, quindi avremo uno sguardo più avanti. Sto comunque cercando un risultato fortemente tipizzato (Raggruppamento) – Ahmad

+0

Quindi sostituisci semplicemente il 'new {}' di 'new StrongType {}'. E istedad di jusing 'Key',' Count' e 'WorkCategoryGroups' usano i membri personalizzati. L'approccio esteso descritto dal link è fortemente digitato. – AxelEckenberger

+0

FYI - il post nella risposta di cui sopra è ottimo - c'è un post di follow up su Dynamic GroupByMany anche se qualcuno è interessato http://blogs.msdn.com/mitsu/archive/2008/02/07/linq -groupbymany-dynamically.aspx – Ahmad

2

userei LINQ sintassi:

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() 
    }; 

Trovo che più facile da leggere rispetto sacco di metodi concatenati con parametri lambda-funzione.

È possibile ottenere i raggruppamenti da questo:

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 concordato, questa sintassi è ** molto ** più facile da leggere quando si fa il raggruppamento. –

+0

@keith, grazie per il suggerimento, il mio primo tentativo di utilizzo sembrava funzionare - tuttavia non è stato testato, tuttavia preferisco l'approccio con metodi concatenati - preferenza personale Immagino :) – Ahmad

0

Ecco un altro modo che non richiede la classe di raggruppamento.

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

Dopo aver provato questo, produce un risultato simile a quello di Obalix risposta. Un raggruppamento per ogni disciplina e categoria di lavoro – Ahmad

0

Questa è la soluzione che funziona per me.

Prima ho avuto bisogno di ottenere tutti i gruppi di disciplina, quindi creare gli elenchi di categorie di lavoro associati in cui ID di disciplina sono uguali.

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() 
       }); 
Problemi correlati