C#LINQ – 如何动态构建Group By子句
我正在处理应用程序,用户可以在其中选择他/她想要在屏幕上看到的列以及要分组或聚合的列。 因此,在我的LINQ部分中,我实际上应该将包含列名的变量传递给group by和aggregate子句。 请记住, DataTable dt
每次都可能包含不同的数据(例如员工信息,采购订单,性能统计等)。 我只能通过dt.Columns[i].ColumnName
和dt.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
给定一组列
// 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
这是牛肉:
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
解决方案是使用我的组件AdaptiveLINQ提供的QueryByCube
函数。 只需使用reflection构建一个Select
表达式。 此表达式由AdaptiveLINQ 自动转换为使用GroupBy
运算符的请求。