如何动态地向LINQ GroupBy子句添加属性

时间:2012-12-03 11:30:59

标签: linq dynamic group-by

我有一个班级Product

class Product
{ 
   int Id { get; set; }
   string Name { get; set; }
   int CategoryId { get; set; }
   int PlantId { get; set; }
   DateTime ProductionDate { get; set; }
}

我想在多个属性上使用LINQ GroupBy但我事先并不知道有多少属性和属性。例如,我可能只想CategoryIdPlantId或两者都进行分组。我在网上发现了一篇描述如何use LINQ GrouBy dinamically的文章。

这可能确实很好但是如果我想在ProductionDate.YearProductionDate.Month上执行分组依据而不事先知道粒度?作为粒度,我的意思是我是否要对特定年份中生成的所有Products进行分组,或者将该组缩小到该月。

我找到的唯一合理解决方案是:

public ProductInfo GetGroupedProducts(int? year, int? month, int? selectedCategoryId, int? selectedPlantId)
{
List<Product> products = GetProducts();

var groupedProducts = products.GroupBy(p => new { (year == null ? p.ProductionDate.Year : string.Empty),
                                                  (month == null ? p.ProductionDate.Month : string.Empty),
                                                  (selectedCategoryId == null ? p.CategoryId : string.Empty),
                                                  (selectedPlantId == null ? p.PlantId : string.Empty)
});

//perform some additional filtering and assignments 

}

但我想可能会有一个更清洁,更合适的解决方案。使用旧式的构建查询的方式,基于字符串,这个任务更容易完成。如果没有别的办法,我真的认为这是LINQ的一部分需要改进。

2 个答案:

答案 0 :(得分:0)

更干净的解决方案是使用此扩展方法:

public static TResult With<TInput, TResult>(this TInput? o, Func<TInput, TResult>
    selector, TResult defaultResult = default(TResult)) where TInput : struct
{
    return o.HasValue ? selector(o.Value) : defaultResult;
}

像这样:

string result = year.With(T => p.ProductionDate.Year, string.Empty);

,如果空值可以:

string result = year.With(T => p.ProductionDate.Year);
如果T有值,则

intint?的内容。


但是,您知道,更好的解决方案是out there,因此请随意扩展您的代码,以便对其进行分析。

答案 1 :(得分:0)

如果我理解你的要求,我也有类似的问题Reversing typeof to use Linq Field<T>

我会做像

这样的事情
public static IEnumerable<IGrouping<string, TElement>> GroupMany<TElement>(
    this IEnumerable<TElement> elements, 
    params Func<TElement, object>[] groupSelectors)
    {
        return elements.GroupBy(e => string.Join(":", groupSelectors.Select(s => s(e))));
    }

然后你可以调用你的函数

var groupedProducts = products.GroupMany(p => p.CategoryId , p => p.ProductionDate.Month);

函数组通过一系列属性除以冒号。之所以这样做是因为字符串的哈希码保证与类相反。