Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#Linq GroupBy方法对于匿名类型和非匿名类型的工作方式不同_C#_Linq_Group By - Fatal编程技术网

C#Linq GroupBy方法对于匿名类型和非匿名类型的工作方式不同

C#Linq GroupBy方法对于匿名类型和非匿名类型的工作方式不同,c#,linq,group-by,C#,Linq,Group By,我有一个包含4列的DataTable,如下所示: private static DataSet dataSet; private const string tableName = "MyTable"; private const string columnName1 = "Supplier"; //Column names private const string columnName2 = "Invoice"; private const string

我有一个包含4列的DataTable,如下所示:

    private static DataSet dataSet;
    private const string tableName = "MyTable";
    private const string columnName1 = "Supplier";  //Column names
    private const string columnName2 = "Invoice";
    private const string columnName3 = "Item";
    private const string columnName4 = "Amount";
我按供应商、发票列对表进行分组,并使用以下linq查询计算金额的总和:

    private static DataTable GroupQueryA(DataTable dataTable)
    {
        DataTable groupedTable = dataTable.AsEnumerable()
            .GroupBy(r => new { Key1 = r.Field<string>(columnName1), Key2 = r.Field<string>(columnName2) })
            .Select(g => new GroupSum
            {
                Key1 = g.Key.Key1,
                Key2 = g.Key.Key2,
                Sum = g.Sum(x => x.Field<double>(columnName4))
            }).PropertiesToDataTable<GroupSum>();

        return groupedTable;
    }
我从中复制的PropertiesToDataTable()方法:

它的工作原理非常完美,因此对于以下表格行:

    AddRow(dataTable, "SA", "INVA", "ITA", 10);
    AddRow(dataTable, "SA", "INVA", "ITB", 20);
    AddRow(dataTable, "SB", "INVB", "ITC", 50);
我在结果中收到两行:

    "SA", "INVA", 30
    "SB", "INVB", 50
但是,我决定修改我的查询,因此我没有使用匿名类型,而是编码了:

    public class GroupKeys
    {
        public string Key1 { get; set; }
        public string Key2 { get; set; }
    }
和查询

    private static DataTable GroupQueryB(DataTable dataTable)
    {
        DataTable groupedTable = dataTable.AsEnumerable()
            .GroupBy(r => new GroupKeys { Key1 = r.Field<string>(columnName1), Key2 = r.Field<string>(columnName2) })
            .Select(g => new GroupSum
            {
                Key1 = g.Key.Key1,
                Key2 = g.Key.Key2,
                Sum = g.Sum(x => x.Field<double>(columnName4))
            }).PropertiesToDataTable<GroupSum>();

        return groupedTable;
    }
看起来源数据根本没有分组,尽管查询中唯一的区别是一行:

    //QueryA
    .GroupBy(r => new { Key1 = r.Field<string>(columnName1), Key2 = r.Field<string>(columnName2) })
    //QueryB
    .GroupBy(r => new GroupKeys { Key1 = r.Field<string>(columnName1), Key2 = r.Field<string>(columnName2) })
//QueryA
.GroupBy(r=>new{Key1=r.Field(columnName1),Key2=r.Field(columnName2)})
//QueryB
.GroupBy(r=>newgroupkeys{Key1=r.Field(columnName1),Key2=r.Field(columnName2)})
有人能解释一下吗? 对于那些想在VisualStudio中运行测试的人,请在下面找到完整的源代码

internal static class TestForStackOverflow
{
    private static DataSet dataSet;
    private const string tableName = "MyTable";
    private const string columnName1 = "Supplier";  //Column names
    private const string columnName2 = "Invoice";
    private const string columnName3 = "Item";
    private const string columnName4 = "Amount";

    private class GroupKeys
    {
        public string Key1 { get; set; }
        public string Key2 { get; set; }
    }

    private class GroupSum
    {
        public string Key1 { get; set; }
        public string Key2 { get; set; }
        public Double Sum { get; set; }
    }

    public static void Test()
    {
        DataTable dataTable = InitializeDataTable();

        //DataTable groupedTable = GroupQueryA(dataTable);      //Please uncomment to run test A
        DataTable groupedTable = GroupQueryB(dataTable);        //Please uncomment to run test B

        DisplayData(groupedTable);
    }


    private static DataTable GroupQueryA(DataTable dataTable)
    {
        DataTable groupedTable = dataTable.AsEnumerable()
            .GroupBy(r => new { Key1 = r.Field<string>(columnName1), Key2 = r.Field<string>(columnName2) })
            .Select(g => new GroupSum
            {
                Key1 = g.Key.Key1,
                Key2 = g.Key.Key2,
                Sum = g.Sum(x => x.Field<double>(columnName4))
            }).PropertiesToDataTable<GroupSum>();

        return groupedTable;
    }

    private static DataTable GroupQueryB(DataTable dataTable)
    {
        DataTable groupedTable = dataTable.AsEnumerable()
            .GroupBy(r => new GroupKeys { Key1 = r.Field<string>(columnName1), Key2 = r.Field<string>(columnName2) })
            .Select(g => new GroupSum
            {
                Key1 = g.Key.Key1,
                Key2 = g.Key.Key2,
                Sum = g.Sum(x => x.Field<double>(columnName4))
            }).PropertiesToDataTable<GroupSum>();

        return groupedTable;
    }

    private static System.Data.DataTable PropertiesToDataTable<T>(this System.Collections.Generic.IEnumerable<T> source)
    {
        System.Data.DataTable dt = new System.Data.DataTable();

        //Weź listę właściwości typu <T> i dla każdej właściwości dodaj do tabeli kolumnę tego samego typu co właściwość
        var props = System.ComponentModel.TypeDescriptor.GetProperties(typeof(T));
        foreach (System.ComponentModel.PropertyDescriptor prop in props)
        {
            System.Data.DataColumn dc = dt.Columns.Add(prop.Name, prop.PropertyType);
            dc.Caption = prop.DisplayName;
            dc.ReadOnly = prop.IsReadOnly;
        }
        //Kopiuj rekordy z kwerendy do DataTable
        foreach (T item in source)
        {
            System.Data.DataRow dr = dt.NewRow();
            foreach (System.ComponentModel.PropertyDescriptor prop in props)
            {
                dr[prop.Name] = prop.GetValue(item);
            }
            dt.Rows.Add(dr);
        }
        return dt;
    }

    private static DataTable InitializeDataTable()
    {
        dataSet = new DataSet();
        DataTable dataTable = dataSet.Tables.Add(tableName);

        dataTable.Columns.Add( columnName1, typeof(string));
        dataTable.Columns.Add( columnName2, typeof(string));
        dataTable.Columns.Add( columnName3, typeof(string));
        dataTable.Columns.Add( columnName4, typeof(double));

        AddRow(dataTable, "SA", "INVA", "ITA", 10);
        AddRow(dataTable, "SA", "INVA", "ITB", 20);
        AddRow(dataTable, "SB", "INVB", "ITC", 50);
        return dataTable;
    }
    private static void AddRow( DataTable dataTable, string supplier, string invoice, string item, double amount)
    {
        DataRow row = dataTable.NewRow();
        row[columnName1] = supplier;
        row[columnName2] = invoice;
        row[columnName3] = item;
        row[columnName4] = amount;
        dataTable.Rows.Add(row);
    }
    private static void DisplayData(System.Data.DataTable table)
    {
        foreach (System.Data.DataRow row in table.Rows)
        {
            foreach (System.Data.DataColumn col in table.Columns)
            {
                Console.WriteLine("{0} = {1}", col.ColumnName, row[col]);
            }
            Console.WriteLine("============================");
        }
    }
}
内部静态类TestForStackOverflow
{
私有静态数据集;
private const string tableName=“MyTable”;
private const string columnName1=“供应商”//列名
private const string columnName2=“发票”;
private const string columnName3=“项”;
private const string columnName4=“Amount”;
私有类组密钥
{
公共字符串Key1{get;set;}
公共字符串Key2{get;set;}
}
私有类组和
{
公共字符串Key1{get;set;}
公共字符串Key2{get;set;}
公共双和{get;set;}
}
公共静态无效测试()
{
DataTable DataTable=InitializeDataTable();
//DataTable groupedTable=GroupQueryA(DataTable);//请取消注释以运行测试A
DataTable groupedTable=GroupQueryB(DataTable);//请取消注释以运行测试B
显示数据(分组表);
}
私有静态数据表GroupQueryA(数据表数据表)
{
DataTable groupedTable=DataTable.AsEnumerable()
.GroupBy(r=>new{Key1=r.Field(columnName1),Key2=r.Field(columnName2)})
.选择(g=>newgroupsum
{
Key1=g.Key.Key1,
Key2=g.Key.Key2,
Sum=g.Sum(x=>x.Field(columnName4))
}).PropertiesToDataTable();
返回分组表;
}
专用静态数据表GroupQueryB(数据表数据表)
{
DataTable groupedTable=DataTable.AsEnumerable()
.GroupBy(r=>newgroupkeys{Key1=r.Field(columnName1),Key2=r.Field(columnName2)})
.选择(g=>newgroupsum
{
Key1=g.Key.Key1,
Key2=g.Key.Key2,
Sum=g.Sum(x=>x.Field(columnName4))
}).PropertiesToDataTable();
返回分组表;
}
私有静态System.Data.DataTable属性StopDataTable(此System.Collections.Generic.IEnumerable源)
{
System.Data.DataTable dt=新的System.Data.DataTable();
//我们列出了一家名为wśaściwości typu i dla kaśdej wśaściwości dodaj do tabeli kolumnśtego samego typu co wśaściwośchi
var props=System.ComponentModel.TypeDescriptor.GetProperties(typeof(T));
foreach(System.ComponentModel.PropertyDescriptor道具中的道具)
{
System.Data.DataColumn dc=dt.Columns.Add(prop.Name,prop.PropertyType);
dc.Caption=prop.DisplayName;
dc.ReadOnly=prop.IsReadOnly;
}
//Kopiuj rekordy z kwerendy do数据表
foreach(源中的T项)
{
System.Data.DataRow dr=dt.NewRow();
foreach(System.ComponentModel.PropertyDescriptor道具中的道具)
{
dr[prop.Name]=prop.GetValue(项目);
}
dt.Rows.Add(dr);
}
返回dt;
}
私有静态数据表InitializeDataTable()
{
数据集=新数据集();
DataTable=dataSet.Tables.Add(tableName);
dataTable.Columns.Add(columnName1,typeof(string));
dataTable.Columns.Add(columnName2,typeof(string));
dataTable.Columns.Add(columnName3,typeof(string));
dataTable.Columns.Add(columnName4,typeof(double));
AddRow(数据表,“SA”、“INVA”、“ITA”,10);
AddRow(数据表,“SA”、“INVA”、“ITB”,20);
AddRow(数据表,“SB”、“INVB”、“ITC”,50);
返回数据表;
}
私有静态void AddRow(数据表、字符串供应商、字符串发票、字符串项目、双倍金额)
{
DataRow行=dataTable.NewRow();
行[columnName1]=供应商;
行[columnName2]=发票;
行[columnName3]=项目;
行[columnName4]=金额;
dataTable.Rows.Add(行);
}
私有静态void DisplayData(System.Data.DataTable)
{
foreach(表中的System.Data.DataRow行)
{
foreach(表列中的System.Data.DataColumn列)
{
Console.WriteLine(“{0}={1}”,col.ColumnName,row[col]);
}
Console.WriteLine(“===========================================”);
}
}
}

您需要稍微润色一下GroupKeys对象。由于它是一个引用类型,并且GroupBy正在使用,所以它的检查的一部分将是测试引用相等性,这将始终返回false

您可以通过覆盖
Equals
GetHashCode
方法来调整GroupKeys类,以测试结构相等性。例如,这是由ReSharper生成的:

private class GroupKeys
{
    public string Key1 { get; set; }
    public string Key2 { get; set; }

    public override int GetHashCode()
    {
        unchecked
        {
            return ((Key1 != null ? Key1.GetHashCode() : 0) * 397) ^ (Key2 != null ? Key2.GetHashCode() : 0);
        }
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj))
            return false;
        if (ReferenceEquals(this, obj))
            return true;
        if (obj.GetType() != this.GetType())
            return false;

        return Equals((GroupKeys)obj);
    }

    public bool Equals(GroupKeys other)
    {
        if (ReferenceEquals(null, other))
            return false;
        if (ReferenceEquals(this, other))
            return true;

        return string.Equals(Key1, other.Key1)
               && string.Equals(Key2, other.Key2);
    }
}

因为C#编译器正确地为匿名类型实现了
GetHashCode
/
Equals
。而对于您自己的类型,您应该自己实现它们。第二种方法是通过一个类{newgroupkeys}进行分组,该类不包含
internal static class TestForStackOverflow
{
    private static DataSet dataSet;
    private const string tableName = "MyTable";
    private const string columnName1 = "Supplier";  //Column names
    private const string columnName2 = "Invoice";
    private const string columnName3 = "Item";
    private const string columnName4 = "Amount";

    private class GroupKeys
    {
        public string Key1 { get; set; }
        public string Key2 { get; set; }
    }

    private class GroupSum
    {
        public string Key1 { get; set; }
        public string Key2 { get; set; }
        public Double Sum { get; set; }
    }

    public static void Test()
    {
        DataTable dataTable = InitializeDataTable();

        //DataTable groupedTable = GroupQueryA(dataTable);      //Please uncomment to run test A
        DataTable groupedTable = GroupQueryB(dataTable);        //Please uncomment to run test B

        DisplayData(groupedTable);
    }


    private static DataTable GroupQueryA(DataTable dataTable)
    {
        DataTable groupedTable = dataTable.AsEnumerable()
            .GroupBy(r => new { Key1 = r.Field<string>(columnName1), Key2 = r.Field<string>(columnName2) })
            .Select(g => new GroupSum
            {
                Key1 = g.Key.Key1,
                Key2 = g.Key.Key2,
                Sum = g.Sum(x => x.Field<double>(columnName4))
            }).PropertiesToDataTable<GroupSum>();

        return groupedTable;
    }

    private static DataTable GroupQueryB(DataTable dataTable)
    {
        DataTable groupedTable = dataTable.AsEnumerable()
            .GroupBy(r => new GroupKeys { Key1 = r.Field<string>(columnName1), Key2 = r.Field<string>(columnName2) })
            .Select(g => new GroupSum
            {
                Key1 = g.Key.Key1,
                Key2 = g.Key.Key2,
                Sum = g.Sum(x => x.Field<double>(columnName4))
            }).PropertiesToDataTable<GroupSum>();

        return groupedTable;
    }

    private static System.Data.DataTable PropertiesToDataTable<T>(this System.Collections.Generic.IEnumerable<T> source)
    {
        System.Data.DataTable dt = new System.Data.DataTable();

        //Weź listę właściwości typu <T> i dla każdej właściwości dodaj do tabeli kolumnę tego samego typu co właściwość
        var props = System.ComponentModel.TypeDescriptor.GetProperties(typeof(T));
        foreach (System.ComponentModel.PropertyDescriptor prop in props)
        {
            System.Data.DataColumn dc = dt.Columns.Add(prop.Name, prop.PropertyType);
            dc.Caption = prop.DisplayName;
            dc.ReadOnly = prop.IsReadOnly;
        }
        //Kopiuj rekordy z kwerendy do DataTable
        foreach (T item in source)
        {
            System.Data.DataRow dr = dt.NewRow();
            foreach (System.ComponentModel.PropertyDescriptor prop in props)
            {
                dr[prop.Name] = prop.GetValue(item);
            }
            dt.Rows.Add(dr);
        }
        return dt;
    }

    private static DataTable InitializeDataTable()
    {
        dataSet = new DataSet();
        DataTable dataTable = dataSet.Tables.Add(tableName);

        dataTable.Columns.Add( columnName1, typeof(string));
        dataTable.Columns.Add( columnName2, typeof(string));
        dataTable.Columns.Add( columnName3, typeof(string));
        dataTable.Columns.Add( columnName4, typeof(double));

        AddRow(dataTable, "SA", "INVA", "ITA", 10);
        AddRow(dataTable, "SA", "INVA", "ITB", 20);
        AddRow(dataTable, "SB", "INVB", "ITC", 50);
        return dataTable;
    }
    private static void AddRow( DataTable dataTable, string supplier, string invoice, string item, double amount)
    {
        DataRow row = dataTable.NewRow();
        row[columnName1] = supplier;
        row[columnName2] = invoice;
        row[columnName3] = item;
        row[columnName4] = amount;
        dataTable.Rows.Add(row);
    }
    private static void DisplayData(System.Data.DataTable table)
    {
        foreach (System.Data.DataRow row in table.Rows)
        {
            foreach (System.Data.DataColumn col in table.Columns)
            {
                Console.WriteLine("{0} = {1}", col.ColumnName, row[col]);
            }
            Console.WriteLine("============================");
        }
    }
}
private class GroupKeys
{
    public string Key1 { get; set; }
    public string Key2 { get; set; }

    public override int GetHashCode()
    {
        unchecked
        {
            return ((Key1 != null ? Key1.GetHashCode() : 0) * 397) ^ (Key2 != null ? Key2.GetHashCode() : 0);
        }
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj))
            return false;
        if (ReferenceEquals(this, obj))
            return true;
        if (obj.GetType() != this.GetType())
            return false;

        return Equals((GroupKeys)obj);
    }

    public bool Equals(GroupKeys other)
    {
        if (ReferenceEquals(null, other))
            return false;
        if (ReferenceEquals(this, other))
            return true;

        return string.Equals(Key1, other.Key1)
               && string.Equals(Key2, other.Key2);
    }
}