LINQ动态分组

我有一个类列表的记录,因此用户可以选择按属性名称动态分组行。 例如MenuTextRoleNameActionName 。 然后我必须执行分组,所以我需要一个通用方法来通过传递列名来处理分组。

示例:

 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(); 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>> GroupMenu(string columnName) { // Here I want to get group the list of menu by the columnName } } 

如果您不使用数据库,可以使用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)助手方法的便捷名称,可以让您编写此类查询。 只需下载源代码 (如果您没有安装VS示例)并在代码中使用它们。

最简单的方法:

 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> GetColumnName(string property) { var menu = Expression.Parameter(typeof(Menu), "menu"); var menuProperty = Expression.PropertyOrField(menu, property); var lambda = Expression.Lambda>(menuProperty, menu); return lambda; } return list.GroupBy(GetColumnName(columnName).Compile()); 

这将产生一个lambda menu => menu.

但是,在课程变得臃肿之前,并没有太大的差异。

以下方法适用于LINQ to Objects以及LINQ to EF / NHibernate等。
它创建一个表达式,该表达式对应于作为字符串传递的列/属性,并将该表达式传递给GroupBy

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

使用基于IQueryable的数据源:

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

使用基于IEnumerable的数据源:

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

BTW:你的方法的返回类型应该是IEnumerable> ,因为IGrouping已经意味着每个键可以有多个 Menu实例。

我按照Adriano的建议用Dynamic Linq做了这个

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

您可以轻松地为任何模型实现解决方案,我刚刚将其设为通用解决方案。

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

所谓的如下

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

非常感谢你的帮助。