我有一个类列表的记录,因此用户可以选择按属性名称动态地对行进行分组。例如MenuText
,RoleName
或ActionName
。然后我必须执行分组,所以我需要一个通用方法来通过传递列名来处理分组。
示例:
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
}
}
答案 0 :(得分:16)
如果您不使用数据库,可以使用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);
编辑 动态 LINQ可能需要一些解释。它不是技术,图书馆或全新框架。它只是一对(2000 LOC)助手方法的便捷名称,可以让您编写此类查询。只需download their source(如果您没有安装VS示例)并在代码中使用它们。
答案 1 :(得分: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());
这将产生一个lambda menu => menu.<PropertyName>
。
但是在课程变得臃肿之前,并没有什么区别。
答案 2 :(得分:3)
以下方法适用于LINQ to Objects以及LINQ to 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());
BTW:您的方法的返回类型应为IEnumerable<IGrouping<string, Menu>>
,因为IGrouping<string, Menu>
已经意味着每个键可以有多个 Menu
个实例。< / p>
答案 3 :(得分:2)
我已按照Adriano
的建议使用Dynamic 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;
}
答案 4 :(得分: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());
非常感谢您的帮助。