2013-07-16 3 views
8

У меня есть список классов записей, поэтому пользователь может выбрать динамическое группирование строк по имени свойства. Например, MenuText, RoleName или ActionName. Затем мне нужно выполнить группировку, поэтому мне нужен общий метод для обработки группировки путем передачи имени столбца.LINQ Группировка динамически

Пример:

public class Menu 
{ 
    public string MenuText {get;set;} 
    public string RoleName {get;set;} 
    public string ActionName {get;set;} 
} 

public class Menus 
{ 
var list = new List<Menu>(); 
list.Add(new Menu {MenuText="abc",RoleName ="Admin", ActionName="xyz"}; 
list.Add(new Menu {MenuText="abc",RoleName ="Admin", ActionName="xyz"}; 
list.Add(new Menu {MenuText="abc1",RoleName ="Admin1", ActionName="xyz1"}; 
list.Add(new Menu {MenuText="abc1",RoleName ="Admin1", ActionName="xyz1"}; 

    /// columnName :- Name of the Menu class (MenuText or RoleName or ActionName) 

    public IEnumerable<IGrouping<string,IEnumerable<Menu>>> GroupMenu(string columnName) 
    { 
      // Here I want to get group the list of menu by the columnName 
    } 
} 

ответ

15

Если вы не работаете с базой данных вы можете использовать Reflection:

private static object GetPropertyValue(object obj, string propertyName) 
{ 
    return obj.GetType().GetProperty(propertyName).GetValue(obj, null); 
} 

Используется как:

var grouped = enumeration.GroupBy(x => GetPropertyValue(x, columnName)); 

Это довольно сырое решение, лучший способ должен использовать Dynamic LINQ :

var grouped = enumeration.GroupBy(columnName, selector); 

EDITDynamic LINQ, возможно, нуждается в некоторых объяснениях. Это не технология, библиотека или совершенно новая структура. Это просто удобное имя для пары (2000 LOC) методов помощников, которые позволяют вам писать такие запросы. Просто download their source (если у вас нет установленных образцов VS) и используйте их в своем коде.

+0

Но мне нужно получить данные из базы данных SQL Server –

+0

, можете ли вы обратиться ко мне любым примером, если у вас есть на динамическом linq –

+0

@ineffablep в последней строке кода в моем ответе (где GroupBy принимает строку как параметр). Дополнительные примеры (и информация) по ссылке (строка перед последним примером кода). –

3

Простейший способ:

if(columnName == "MextText") 
{ 
    return list.GroupBy(x => x.MenuText); 
} 

if(columnName == "RoleName") 
{ 
    return list.GroupBy(x => x.RoleName); 
} 

if(columnName == "ActionName") 
{ 
    return list.GroupBy(x => x.ActionName); 
} 

return list.GroupBy(x => x.MenuText); 

Вы можете также использовать деревья выражений.

private static Expression<Func<Menu, string>> GetColumnName(string property) 
{ 
    var menu = Expression.Parameter(typeof(Menu), "menu"); 
    var menuProperty = Expression.PropertyOrField(menu, property); 
    var lambda = Expression.Lambda<Func<Menu, string>>(menuProperty, menu); 

    return lambda; 
} 

return list.GroupBy(GetColumnName(columnName).Compile()); 

Это произведет лямбда menu => menu.<PropertyName>.

Но на самом деле разница не очень большая, пока класс не раздувается.

+1

но это пример, я ищу универсальное решение для реализации для любой модели –

+0

Мне нравится, что Romoku всегда имеет полный ответ быстро –

2

Следующий подход будет работать с LINQ к объектам, а также с помощью LINQ к EF/NHibernate/и т.д.
Это создает выражение, которое соответствует колонке/свойства, переданного в качестве строки и передает, что выражение GroupBy:

private static Expression<Func<Menu, string>> GetGroupKey(string property) 
{ 
    var parameter = Expression.Parameter(typeof(Menu)); 
    var body = Expression.Property(parameter, property); 
    return Expression.Lambda<Func<Menu, string>>(body, parameter); 
} 

Использование с IQueryable<T> на основе источников данных:

context.Menus.GroupBy(GetGroupKey(columnName)); 

Использование с IEnumerable<T> на основе источников данных:

list.GroupBy(GetGroupKey(columnName).Compile()); 

КСТАТИ: Возвращаемый тип вашего метода должен быть IEnumerable<IGrouping<string, Menu>>, так как IGrouping<string, Menu> уже означает, что может быть множественнымMenu экземпляров для каждого ключа.

+0

как сделать, когда это «динамический», т. Е. Я не знаю, что это «Меню»? Я не могу делать typeof (dynamic), и если я делаю typeof (object), то он будет жаловаться, что свойство не существует в System.Object. –

+0

Вам нужно будет передать экземпляр и вызвать GetType на нем –

+0

У меня есть мешок объектов Expando, который является ничем иным, как ключевым значением пары строк. Какой экземпляр я должен пройти здесь. Нет ничего конкретного –

0

Я сделал это с динамическим Linq как предложил Адриано

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

public static IEnumerable<IGrouping<object, TElement>> GroupByMany<TElement>(
     this IEnumerable<TElement> elements, params Func<TElement, object>[] groupSelectors) 
    { 
     if (groupSelectors.Length > 0) 
     { 
      Func<TElement, object> selector = groupSelectors.First(); 
      return elements.GroupBy(selector); 
     } 
     return null; 
    } 
0

Вы решение легко реализовать для любой модели, я только что сделал это родовое один.

public static Expression<Func<TElement, string>> GetColumnName<TElement>(string property) 
    { 
     var menu = Expression.Parameter(typeof(TElement), "groupCol"); 
     var menuProperty = Expression.PropertyOrField(menu, property); 
     var lambda = Expression.Lambda<Func<TElement, string>>(menuProperty, menu); 
     return lambda; 
    } 

так назвал это, как показано ниже

_unitOfWork.MenuRepository.Get().GroupBy(LinqExtensions.GetColumnName<Menu>("MenuText").Compile()); 

Большое спасибо за помощь.

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