C#LINQ - 如何建立Group By子句动态 [英] C# LINQ - How to build Group By clause dynamically

查看:233
本文介绍了C#LINQ - 如何建立Group By子句动态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的工作的应用程序,其中用户可以选择列,他/她想要看到的画面和列GROUP BY或集合上。所以,在我的LINQ节中,我实际上应该通过持有列名GROUP BY和聚合从句两个变量。请记住, DataTable的DT 可能持有不同的数据,每次(例如,员工信息,采购订单,性能统计等)。我只能通过 dt.Columns [I] .ColumnName dt.Columns [I] .DataType.Name得到有关在运行时的数据信息。任何一个可以告诉我如何做到这一点,我需要的是这样的:

  SqlDataAdapter的大=新SqlDataAdapter的(CMD); 
DataTable的DT =新的DataTable();
da.Fill(DT);

VAR的查询=从行dt.AsEnumerable()被新的
{
的foreach(在dt.Columns的DataColumn列)
{
组一行
行[column.ColumnName]
}
}到GRP

选择新的
{
的foreach(在dt.Columns的DataColumn列)
{
如果(column.DataType.Name ==十进制)
{
总和(GRP [column.ColumnName]);
}其他{
GRP [column.ColumnName]
}

}
};


解决方案

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



下面是一类我用漂亮的通常被称为NTuple。这是同样的想法的元组LT;吨>中元组LT; T1,T2和附带的.NET框架GT ;,等类。然而,NTuple类被设计容纳可变数量的项目。两个NTuple实例都是平等的,如果它们包含相同数量的值,并将这些值是相等的。



给定一组列

  //按OP,成组的字段通过在运行时将
IEnumerable的<来产生列表;串> columnsToGroupBy = ...;

您可以通过这样的列使用NTuple类组:

  VAR组= dt.AsEnumerable()
.GroupBy(R =>新建NTuple<对象>在columnsToGroupBy(从列选择R [列]));



这里的牛肉:

 公共类NTuple< T> :IEquatable< NTuple< T>> 
{
公共NTuple(IEnumerable的< T>的值)
{
值= values​​.ToArray();
}

公共只读T []值;

公众覆盖布尔等于(obj对象)
{
如果(的ReferenceEquals(这一点,OBJ))
返回真;
如果(OBJ == NULL)
返回false;
收益等于(OBJ作为NTuple< T>);
}

公共布尔等于(NTuple< T>其他)
{
如果(的ReferenceEquals(这一点,其他))
返回真;
如果(其他== NULL)
返回false;
VAR长度= Values​​.Length;
如果(长度= other.Values​​.Length!)
返回false;
为(VAR I = 0; I<长度; ++ I)
如果
返回假(等于(值[I],other.Values​​ [I])!);
返回真;
}

公共覆盖INT的GetHashCode()
{
VAR HC = 17;
的foreach(中值VAR值)
HC = HC * 37 +(的ReferenceEquals(值null)value.GetHashCode():?0);
返回HC;
}
}

下面是一个测试用例:

 静态无效的主要(字串[] args)
{
//一些样本数据
变种dt的=新的DataTable ();
dt.Columns.Add(NAME的typeof(字符串));
dt.Columns.Add(CITY的typeof(字符串));
dt.Columns.Add(国家,typeof运算(字符串));
dt.Columns.Add(VALUE的typeof(双));
dt.Rows.Add(迈克,塔拉哈西,FL,3);
dt.Rows.Add(迈克,塔拉哈西,FL,6);
dt.Rows.Add(史蒂夫,塔拉哈西,FL,5);
dt.Rows.Add(史蒂夫,塔拉哈西,FL,10);
dt.Rows.Add(史蒂夫,奥兰多,FL,7);
dt.Rows.Add(史蒂夫,奥兰多,FL,14);
dt.Rows.Add(迈克,奥兰多,NY,11);
dt.Rows.Add(迈克,奥兰多,NY,22);

//一些配置的数据
IEnumerable的<串GT; columnsToGroupBy =新[] {城市,国家};
串columnToAggregate =VALUE;

//测试例程
的foreach(在dt.AsEnumerable变种组()GROUPBY(R =方式>新NTuple&所述;对象>(从塔中columnsToGroupBy选择R [柱]) ))
{
的foreach(在group.Key.Values​​ VAR的keyValue)
{
Debug.Write(的keyValue);
Debug.Write(':');
}
的Debug.WriteLine(group.Sum(R => Convert.ToDouble(R [columnToAggregate])));
}
}


I am working on the application where user can select columns he/she wants to see on the screen and which columns to group by or aggregate. So, in my LINQ section I should actually pass variables that hold column names to both group by and aggregate clause. Keep in mind that DataTable dt may hold different data every time(e.g. Employee info, Purchase orders, Performance stats, etc). I can only get information about the data at run time via dt.Columns[i].ColumnName and dt.Columns[i].DataType.Name. Can any one advise how to do that, what I need is something like this:

        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];
                           }

                        }
                    };

解决方案

There are several ways to do this. Here's one.

Below is a class I use pretty often called NTuple. It is the same idea as the Tuple<T>, Tuple<T1, T2>, etc classes that come with the .NET framework. However, the NTuple class is designed to hold a variable number of items. Two NTuple instances are equal if they contain the same number of values and those values are equal.

Given a set of columns

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

you can use the NTuple class to group by those columns like this:

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

Here's the beef:

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;
    }
}

Here's a test case:

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])));
    }
}

这篇关于C#LINQ - 如何建立Group By子句动态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆