C# LINQ动态分组
我有一个记录的类列表,所以用户可以选择按属性名动态分组行。例如C# LINQ动态分组,c#,.net,linq,group-by,C#,.net,Linq,Group By,我有一个记录的类列表,所以用户可以选择按属性名动态分组行。例如MenuText,RoleName或ActionName。然后我必须执行分组,所以我需要一个通用方法来通过传递列名来处理分组 例如: public class Menu { public string MenuText {get;set;} public string RoleName {get;set;} public string ActionName {get;set;} } public class Menus
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
}
}
公共类菜单
{
公共字符串MenuText{get;set;}
公共字符串RoleName{get;set;}
公共字符串ActionName{get;set;}
}
公共类菜单
{
var list=新列表();
添加(新菜单{MenuText=“abc”,RoleName=“Admin”,ActionName=“xyz”};
添加(新菜单{MenuText=“abc”,RoleName=“Admin”,ActionName=“xyz”};
添加(新菜单{MenuText=“abc1”,RoleName=“Admin1”,ActionName=“xyz1”};
添加(新菜单{MenuText=“abc1”,RoleName=“Admin1”,ActionName=“xyz1”};
///columnName:-菜单类的名称(MenuText或RoleName或ActionName)
公共IEnumerable组菜单(字符串列名称)
{
//在这里,我想按列名对菜单列表进行分组
}
}
最简单的方法:
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());
私有静态表达式GetColumnName(字符串属性)
{
变量菜单=表达式参数(类型(菜单),“菜单”);
var menuperty=Expression.PropertyOrField(菜单,属性);
var lambda=Expression.lambda(菜单属性,菜单);
返回lambda;
}
return list.GroupBy(GetColumnName(columnName.Compile());
这将产生一个lambda菜单=>菜单。
但是,在类变得臃肿之前,实际上没有多大区别。如果不使用数据库,可以使用反射:
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可能需要一些解释。它不是一种技术、一个库或一个全新的框架。它只是一个方便的名称,用于几个(2000 LOC)helpers方法,让您编写这样的查询。只是(如果您没有安装VS示例)并在代码中使用它们。以下方法适用于LINQ to对象以及LINQ to EF/NHibernate/etc。
它创建一个表达式,该表达式对应于作为字符串传递的列/属性,并将该表达式传递给
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);
}
与基于IEnumerable
的数据源一起使用:
context.Menus.GroupBy(GetGroupKey(columnName));
list.GroupBy(GetGroupKey(columnName).Compile());
顺便说一句:您的方法的返回类型应该是
IEnumerable
,因为iGroup
已经意味着每个键可以有多个菜单
实例。我已经按照Adriano的建议使用动态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;
}
公共静态IEnumerable GroupByMany(
此IEnumerable元素、参数字符串[]groupSelectors)
{
变量选择器=新列表(groupSelectors.Length);
selectors.AddRange(groupSelectors.Select(selector=>DynamicExpression.ParseLambda(typeof(TElement)、typeof(object)、selector)).Select(l=>(Func)l.Compile());
返回elements.GroupByMany(selectors.ToArray());
}
公共静态IEnumerable GroupByMany(
这是IEnumerable元素,参数Func[]groupselector)
{
如果(groupSelectors.Length>0)
{
Func selector=groupSelectors.First();
返回元素.GroupBy(选择器);
}
返回null;
}
您的解决方案对于任何模型都很容易实现,我刚刚将其作为通用解决方案
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;
}
公共静态表达式GetColumnName(字符串属性)
{
变量菜单=表达式参数(typeof(TElement),“groupCol”);
var menuperty=Expression.PropertyOrField(菜单,属性);
var lambda=Expression.lambda(菜单属性,菜单);
返回lambda;
}
这就是所谓的下文
_unitOfWork.MenuRepository.Get().GroupBy(LinqExtensions.GetColumnName<Menu>("MenuText").Compile());
\u unitOfWork.MenuRepository.Get().GroupBy(LinqExtensions.GetColumnName(“MenuText”).Compile());
非常感谢您的帮助。但是,这是一个例子,我正在寻找一个通用的解决方案来实现任何模型,像RoMuKu总是有一个完整的答案FAST,但是我需要从数据库SQL Server中检索数据。不幸的是,动态LINQ多年来一直没有被更新,这就是为什么我不考虑推荐它。只是在标准LINQ实现之上构建的几个helpers类的名称。没有什么需要更新的…它是通过社区(非正式)进行更新的-位于此处:-但是@AdrianoRepetti是正确的,确实没有太多需要更新的,它只是标准linq.Tnx@Traviswhiden上的一个不错的层,很好的链接:他还添加了一些IMO在原始代码中缺少的示例(作为单元测试)。我更新了答案,指向那个repo!当它是“动态”的时候该怎么办,也就是说,我不知道它是“菜单”?我不能做typeof(动态)和如果我做typeof(对象)然后它会抱怨System.Object上不存在属性。你需要传递一个实例并在itI上调用GetType。我有一包Expando对象,它们只不过是一对键值字符串。我应该在这里传递什么实例。没有具体的内容。请问一个新问题。这超出了评论的范围,但基本上ExpandoObject实现了
IDictionary
,因此您应该能够做如下简单的事情:listOfYourExpandoObjects.GroupBy(x=>x[columnName])
在第二个GroupByMany方法中,您有GroupSelector.First();因此,分组仅对传递到第一个GroupByMany方法的params数组的string属性执行。因此,我没有得到一个要点。。
_unitOfWork.MenuRepository.Get().GroupBy(LinqExtensions.GetColumnName<Menu>("MenuText").Compile());