C#LINQ – 如何动态构建Group By子句

我正在处理应用程序,用户可以在其中选择他/她想要在屏幕上看到的列以及要分组或聚合的列。 因此,在我的LINQ部分中,我实际上应该将包含列名的变量传递给group by和aggregate子句。 请记住, DataTable dt每次都可能包含不同的数据(例如员工信息,采购订单,性能统计等)。 我只能通过dt.Columns[i].ColumnNamedt.Columns[i].DataType.Name在运行时获取有关数据的信息。 任何人都可以建议如何做到这一点,我需要的是这样的事情:

  SqlDataAdapter da = new SqlDataAdapter(cmd); DataTable dt = new DataTable(); da.Fill(dt); var query = from row in dt.AsEnumerable() group row by new { foreach(DataColumn column in dt.Columns) { row[column.ColumnName]; } } into grp select new { foreach(DataColumn column in dt.Columns) { if(column.DataType.Name == "Decimal") { Sum(grp[column.ColumnName]); }else{ grp[column.ColumnName]; } } }; 

有几种方法可以做到这一点。 这是一个。

下面是我经常使用的一个名为NTuple的类。 它与.NET框架附带的Tuple ,Tuple 等类相同。 但是,NTuple类旨在容纳可变数量的项目。 如果两个NTuple实例包含相同数量的值并且这些值相等,则它们是相等的。

给定一组列

 // as per OP, the list of columns to group by will be generated at runtime IEnumerable columnsToGroupBy = ...; 

您可以使用NTuple类按这样的列进行分组:

 var groups = dt.AsEnumerable() .GroupBy(r => new NTuple(from column in columnsToGroupBy select r[column])); 

这是牛肉:

 public class NTuple : IEquatable> { public NTuple(IEnumerable values) { Values = values.ToArray(); } public readonly T[] Values; public override bool Equals(object obj) { if (ReferenceEquals(this, obj)) return true; if (obj == null) return false; return Equals(obj as NTuple); } public bool Equals(NTuple other) { if (ReferenceEquals(this, other)) return true; if (other == null) return false; var length = Values.Length; if (length != other.Values.Length) return false; for (var i = 0; i < length; ++i) if (!Equals(Values[i], other.Values[i])) return false; return true; } public override int GetHashCode() { var hc = 17; foreach (var value in Values) hc = hc*37 + (!ReferenceEquals(value, null) ? value.GetHashCode() : 0); return hc; } } 

这是一个测试用例:

 static void Main(string[] args) { // some sample data var dt = new DataTable(); dt.Columns.Add("NAME", typeof(string)); dt.Columns.Add("CITY", typeof(string)); dt.Columns.Add("STATE", typeof(string)); dt.Columns.Add("VALUE", typeof(double)); dt.Rows.Add("Mike", "Tallahassee", "FL", 3); dt.Rows.Add("Mike", "Tallahassee", "FL", 6); dt.Rows.Add("Steve", "Tallahassee", "FL", 5); dt.Rows.Add("Steve", "Tallahassee", "FL", 10); dt.Rows.Add("Steve", "Orlando", "FL", 7); dt.Rows.Add("Steve", "Orlando", "FL", 14); dt.Rows.Add("Mike", "Orlando", "NY", 11); dt.Rows.Add("Mike", "Orlando", "NY", 22); // some "configuration" data IEnumerable columnsToGroupBy = new[] {"CITY", "STATE"}; string columnToAggregate = "VALUE"; // the test routine foreach (var group in dt.AsEnumerable().GroupBy(r => new NTuple(from column in columnsToGroupBy select r[column]))) { foreach (var keyValue in group.Key.Values) { Debug.Write(keyValue); Debug.Write(':'); } Debug.WriteLine(group.Sum(r => Convert.ToDouble(r[columnToAggregate]))); } } 

解决方案是使用我的组件AdaptiveLINQ提供的QueryByCube函数。 只需使用reflection构建一个Select表达式。 此表达式由AdaptiveLINQ 自动转换为使用GroupBy运算符的请求。