2015-05-14 2 views
1

Я пытаюсь использовать Linq для группировки List<Person>.Linq GroupBy Анонимный тип с динамическими свойствами

var grouped = personList.GroupBy(x => new { x.Forename, x.Age }) 
         .Select(x => new { Description = x.Key, Count = x.Count() }); 

Как я могу создать свойства, которые будут сгруппированы? Из переменной?

var groupByProperties = new string[] { "Forename", "Age" }; 
personList.GroupBy(x => new { ............ }) 
      .Select(x => new { Description = x.Key, Count = x.Count() }); 

groupByProperties выйдет из пользовательского ввода на веб-странице (<select multiple>).

Я не могу получить главу вокруг правильного синтаксиса для ExpandoObject или dynamic и не уверен, что мне нужно использовать Reflection здесь.

Класс Person может выглядеть примерно так:

public class Person 
{ 
    public string Forename { get; set; } 
    public string Surname { get; set; } 
    public int Age { get; set; } 
    public string Gender { get; set; } 
} 

Результат передается обратно через к пользовательскому интерфейсу (веб-страницы), как JSON, из которого будет сформирована DataGrid. Я буду использовать javascript для прокрутки возвращаемого объекта Description и генерации сетки из этого.

+0

Не используйте ExpandoObject в LINQ, как это может произойти утечка памяти – dr4cul4

+2

Это не ясно, как вы хотите, чтобы это быть динамичным - это еще выбор времени компиляции? Вам нужен ключ * whole * group как свойство Description? Что вы делаете после этого? –

+2

@ dr4cul4: Уточните и/или предоставите ссылки/доказательства? –

ответ

1

С благодаря potehin143 указал мне в сторону ScottGu отличными Linq Dynamic Query Library, мне удалось получить что-то работает.

Я получил отличный однострочный (хотя и слегка причудливый синтаксис).

var fieldsToGroupBy = new string[] { "Forename", "Age" }; 

var grouped = personList.GroupBy("new ("+fieldsToGroupBy.ToCommaSeparatedString("it.")+")", "it") 
       .Select("new (it.Key as Description, it.Count() as Count)"); 

response.Data = (dynamic)grouped; 

ToCommaSeparatedString() простой метод расширения для изменения [ "Forename", "Возраст"] в "it.Forename, it.Age".

Предыдущее решение (ниже) не возвращало данные в таком формате, который я надеялся. (Он возвращал все данные в группах, а не просто сводку.) Кредит Mitsu для этого решения (сообщения блога here и here).

var groupByProperties = new string[] { "Forename", "Age" }; 
var grouped = personList.GroupByMany(groupByProperties); 

Библиотека динамического запроса Linq делает слишком много для публикации даже фрагмента. Метод GroupByMany:

public static IEnumerable<GroupResult> GroupByMany<TElement>(
     this IEnumerable<TElement> elements, params string[] groupSelectors) 
{ 
    var selectors = 
     new List<Func<TElement, object>>(groupSelectors.Length); 
    foreach (var selector in groupSelectors) 
    { 
     LambdaExpression l = 
      DynamicExpression.ParseLambda(
       typeof(TElement), typeof(object), selector); 
     selectors.Add((Func<TElement, object>)l.Compile()); 
    } 
    return elements.GroupByMany(selectors.ToArray()); 
} 

public static IEnumerable<GroupResult> GroupByMany<TElement>(
    this IEnumerable<TElement> elements, 
    params Func<TElement, object>[] groupSelectors) 
{ 
    if (groupSelectors.Length > 0) 
    { 
     var selector = groupSelectors.First(); 

     //reduce the list recursively until zero 
     var nextSelectors = groupSelectors.Skip(1).ToArray(); 
     return 
      elements.GroupBy(selector).Select(
       g => new GroupResult 
       { 
        Key = g.Key, 
        Count = g.Count(), 
        Items = g, 
        SubGroups = g.GroupByMany(nextSelectors) 
       }); 
    } 
    else 
     return null; 
} 
-1

Попробуйте

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace ConsoleApplication29 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      List<Person> personList = new List<Person>(); 
      string[] groups = { "Forename", "Surname" }; 
      var grouped = personList.GroupBy(x => new object[] { Grouper(x, groups) }) 
         .Select(x => new { Description = x.Key, Count = x.Count() }); 
     } 
     static object[] Grouper(Person person, string[] groups) 
     { 
      List<object> results = new List<object>(); 
      foreach (string group in groups) 
      { 
       switch (group) 
       { 
        case "Forename" : 
         results.Add(person.Forename); 
         break; 
        case "Surname": 
         results.Add(person.Surname); 
         break; 
        case "Age": 
         results.Add(person.Age); 
         break; 
        case "Gender": 
         results.Add(person.Gender); 
         break; 
       } 
      } 


      return results.ToArray(); 
     } 
    } 
    public class Person 
    { 
     public string Forename { get; set; } 
     public string Surname { get; set; } 
     public int Age { get; set; } 
     public string Gender { get; set; } 
    } 
} 
+0

'EqualityComparer .Default' будет использовать ссылку массива как идентификатор, а не значения массива, поэтому при использовании этого решения каждое значение, которое вы группируете, всегда будет уникальным. – Servy

Смежные вопросы