12

我有一个记录类列表,因此用户可以选择按属性名称动态分组行。例如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<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 
  }
}
4

5 回答 5

23

如果您不使用数据库,则可以使用反射:

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 示例)并在您的代码中使用它们。

于 2013-07-16T13:48:51.127 回答
5

最简单的方法:

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>

但是在类变得臃肿之前并没有太大的区别。

于 2013-07-16T13:47:27.443 回答
5

以下方法适用于 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());

顺便说一句:您的方法的返回类型应该是IEnumerable<IGrouping<string, Menu>>,因为 anIGrouping<string, Menu>已经意味着每个键可以有多个 Menu实例。

于 2013-07-16T14:01:19.390 回答
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;
    }
于 2013-07-16T14:25:09.837 回答
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());

非常感谢你的帮助。

于 2013-07-16T14:37:37.540 回答