2010-02-28 16 views
6

considerar los siguientes objetos simplificados y relaciones:Cómo agrupar varios elementos 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; 
} 

Los modelos anteriores de una relación que una Job está asociada con una Discipline y uno WorkCategory. El Workcategory siempre es hijo de un padre específico Discipline (relación de uno a muchos). Podemos suponer que la relación entre la Disciplina asignada y la Categoría de trabajo siempre será válida.

El problema al que me enfrento es cuando intento crear un objeto de resultado Grouping basado en los filtros que se aplican a Jobs. No estoy seguro de si esto puede hacerse o si el enfoque que estoy tomando es incluso correcto. La pregunta exacta en sí no es clara para mí, sin embargo, lo anterior define la declaración del problema.

  1. ¿Se puede mejorar el diseño?
  2. ¿Cómo puedo agrupar trabajos por disciplina y categoría de trabajo?
  3. ¿Necesito siquiera la clase Grouping?

He intentado lo siguiente (este es mi primer intento de usar Linq), pero sin éxito ya que mi comprensión no es lo suficientemente completa. La otra alternativa es obtener primero el grupo Discipline y recorrer la agrupación original seleccionando el relacionado 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 
          }}); 

Editar: Después de haber publicado esto, me di cuenta de que los parámetros GroupBy Inital dan lugar a un grupo de punto mientras que yo estoy buscando resultados del grupo-subgrupo.

Edición 2: Para clarificar un poco más lejos, no quiero The Associated Jobs como parte del resultado, pero agrupación más bien la disciplina-Workcategory - por lo tanto la razón de la clase Grouping


Inicial solución basada en @Obalix

Editar 7-Mar-2010:

Esta solución no funciona: el GroupBy en el objeto Discipline generará una agrupación única para cada objeto. Creo que esto se debe a que es un tipo de referencia. ¿Estoy en lo correcto? Al principio acepté esto como la respuesta, sin embargo, después de algunos arañazos en la cabeza, me di cuenta de que mis propios datos falsos estaban defectuosos. Las preguntas iniciales siguen siendo respondidas.

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

Respuesta

2

Here is a description cómo puede implementar un mecanismo de agrupación jerárquica.

una forma genérica de hacer esto usando LINQ (como se muestra en el enlace) es:

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

Sin embargo, el enlace también describe una forma alternativa que le permite hacer lo siguiente:

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

Editar: siguiente comentario de Ahmad aquí es la versión estricta de tipos:

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

He echado un vistazo al enlace y la lectura de los comentarios me llevó a otra publicación, así que echaremos un vistazo a eso. Sin embargo, estoy buscando un resultado fuertemente tipado (Agrupación) – Ahmad

+0

Luego simplemente reemplaza el 'nuevo {}' por 'nuevo StrongType {}'. E istedad de jusing 'Key',' Count', y 'WorkCategoryGroups' usa sus miembros personalizados. El enfoque extendido descrito por el enlace está fuertemente tipado. – AxelEckenberger

+0

FYI - lo publicado en la respuesta anterior es excelente - hay una publicación de seguimiento sobre Dynamic GroupByMany también si alguien está interesado http://blogs.msdn.com/mitsu/archive/2008/02/07/linq -groupbymany-dynamically.aspx – Ahmad

2

que haría uso de LINQ sintaxis:

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

encuentro que facilita la lectura de un montón de métodos encadenados con parámetros lambda-función.

Usted puede conseguir sus agrupaciones de esto:

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 De acuerdo, esta sintaxis es ** mucho ** más fácil de leer al hacer la agrupación. –

+0

@keith, gracias por la sugerencia, mi primer intento de usarlo pareció funcionar; sin embargo, no fue probado, sin embargo, prefiero el enfoque de métodos encadenados. Preferencia personal :) – Ahmad

0

Ésta es otra manera que no requiere la clase de agrupación.

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

Después de probar esto, arroja un resultado similar al de Obalix responder. Una agrupación para cada disciplina y categoría de trabajo – Ahmad

0

Esta es la solución que funciona para mí.

Primero necesitaba obtener todos los grupos de disciplinas, luego crear las listas de categorías de trabajos asociadas donde los disciplineID son iguales.

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