Linq to Sql 按类分组

本文关键字:to Sql Linq | 更新日期: 2023-09-27 18:29:32

我想将 groupedList 传递给几个函数,但是当尝试使用类而不是匿名类型进行分组时,我得到了奇怪的结果。在下面的示例中,匿名类型按预期返回 2 个结果,但具体类返回 5,就好像它甚至没有分组一样。

问题:是否可以使用具体类将Linq更改为Sql Group By?

public class person
{
    public string Name;
    public string State;
}
public class personGroup
{
    public string State;
    public personGroup(string personState)
    {
        State = personState;
    }
}

void Main()
{   
    var people = new List<person>();
    people.Add(new person {Name = "Bob", State = "Tx"});
    people.Add(new person {Name = "Bill", State = "Tx"});
    people.Add(new person {Name = "Tracy", State = "Tx"});
    people.Add(new person {Name = "Steve", State = "Md"});
    people.Add(new person {Name = "Kelly", State = "Md"});
    var groupedPeople = people.GroupBy (p => p.State );
    groupedPeople.Count().Dump();//Returns 2
    var morePeople = people.GroupBy (p => new personGroup(p.State) );
    morePeople.Count().Dump();//Returns 5
}

Linq to Sql 按类分组

当未提供自定义IEqualityComparer(您未提供自定义(时,GroupBy 方法使用EqualityComparer<T>.Default来比较有问题的项目。 这将基于相关T类型的IEquatable<T>的实现,如果有,如果没有,那么它将仅使用该类型的object.Equalsobject.GetHashCode

您的自定义类型不提供任何实现,它完全依赖于 object 中定义的实现,对于引用类型,它基于对象的引用。 您创建的每个personGroup对象都有不同的引用,因此是不同的。

匿名类型不使用默认的相等性;它们重写Equals的定义,GetHashCode依赖于它们所表示的每个属性的标识。

如果要使用自己的自定义类型进行分组,并且默认相等语义不是您想要的,则需要提供自定义IEqualityComparer实现,或重写相关类型的EqualsGetHashCode

在第二组中,有 5 组,因为键总是不同的。 personGroups按引用进行比较,并且所有对象都有不同的引用。您需要重写 personGroup 类中的 EqualsGetHashCode 方法,以根据 State 比较实例,或者实现IEqualityComparer<personGroup>并将其传递给 GroupBy

正如 Servy 上面所说,您需要创建一个类型为 PersonGroup 的自定义 IEqualityComparer 才能使您的示例正常工作。

我自己也遇到过很多,所以我创建了一个通用的 EqualityComparer,它可以与任何模型(使用属性(一起使用!

/// <summary>
/// Given two models, return TRUE if all the properties are equal,
/// else return FALSE
/// </summary>
/// <typeparam name="TModel">Type of the models being compared</typeparam>
public class PropertyEqualityComparer<TModel> : IEqualityComparer<TModel>
{
    public bool Equals(TModel x, TModel y)
    {
        PropertyInfo[] props = typeof(TModel).GetProperties();
        foreach (PropertyInfo prop in props)
        {
            if (!Object.Equals(prop.GetValue(x), prop.GetValue(y)))
            {
                return false;
            }    
        }
        return true;
    }
    public int GetHashCode(TModel obj)
    {
        int hash = 1;
        PropertyInfo[] props = typeof(TModel).GetProperties();
        foreach (PropertyInfo prop in props)
        {
            hash ^= prop.GetValue(obj).GetHashCode();
        }
        return hash;
    }
}

有了这个类,你将能够做你做什么。

// NOTE: Use properties instead of public variables
public class Person
{
    public string Name { get; set; }
    public string State { get; set; }
}
// NOTE: Use properties instead of public variables
public class personGroup
{
    public string State { get; set; }
}
void Main()
{   
    var people = new List<Person>();
    people.Add(new Person{Name = "Bob", State = "Tx"});
    people.Add(new Person{Name = "Bill", State = "Tx"});
    var morePeople = people.GroupBy(p => new PersonGroup{State = p.State}, new PropertyEqualityComparer<PersonGroup>());
    morePeople.Count();
}