【问题标题】:C# LINQ - How to build Group By clause dynamicallyC# LINQ - 如何动态构建 Group By 子句
【发布时间】:2014-12-26 20:19:56
【问题描述】:

我正在开发应用程序,用户可以在该应用程序中选择他/她想在屏幕上看到的列以及要分组或聚合的列。因此,在我的 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];
                           }

                        }
                    };

【问题讨论】:

标签: c# linq group-by aggregate dynamic-linq


【解决方案1】:

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

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

给定一组列

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

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

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

这是牛肉:

public class NTuple<T> : IEquatable<NTuple<T>>
{
    public NTuple(IEnumerable<T> 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<T>);
    }

    public bool Equals(NTuple<T> 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<string> columnsToGroupBy = new[] {"CITY", "STATE"};
    string columnToAggregate = "VALUE";

    // the test routine
    foreach (var group in dt.AsEnumerable().GroupBy(r => new NTuple<object>(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])));
    }
}

【讨论】:

  • Michael,分组效果很好,但在选择部分我遇到了问题,foreach (var keyValue in group.Key.Values) group.Key 似乎没有属性值。有什么想法吗?
  • 我刚刚将您的代码复制并粘贴到我页面上的按钮单击事件中,它无法识别 group.Key 的 Values 属性,我使用的是 Visual Stidio 2013, 4.5 框架。感谢您对我的耐心。
  • 哦,废话!对不起。我在帖子中途更改了 NTuple 课程,却忘记更新帖子。我刚刚修好了。见上文。
  • 迈克尔,我只是在尝试您的示例,我已在原始问题的更新下添加了它。
  • 是的。请注意,我更改了答案。另外,请将更改恢复到您的原始帖子,因为它会混淆原始问题并使某人难以确定您在问什么。
【解决方案2】:

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

【讨论】:

  • 也许我不应该投反对票,但由于我实际上是在寻找这个问题的好答案,所以在我看来,你真的没有发布任何有用的信息,除了插入你自己的商业广告产品。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-06-06
  • 2011-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-16
相关资源
最近更新 更多