如何使用LINQ和C#使用方法语法获得一个独特的结果

时间:2013-03-08 18:43:40

标签: c# linq

以下代码仍未返回DISTINCT结果集。我试图完成的等效SQL是SELECT DISTINCT LEFT(Fac_Name, 6) AS ID, LEFT(Fac_Name, 3) AS Fac_Name

 public List<Facility> GetFacilities() {
        var facilities = new List<Facility>(); 
        facilities = _facilityRepository.GetAll().ToList();
        var facReturnList = 
            facilities.Where(x => x.Fac_Name = "Something")
                      .OrderBy(x => x.Fac_Name).ToList();

        var facReturnList2 = 
            facReturnList.Select(x => 
                new Facility { ID = x.Fac_Name.Substring(0, 6), 
                      Fac_Name = x.Fac_Name.Substring(0, 3) })
                .Distinct().ToList();
        return facReturnList2;
    }

3 个答案:

答案 0 :(得分:2)

您遇到的问题是您正在创建不同的引用值(将返回不同的哈希码),即使每个引用中的属性相等,实际引用本身也是不同的。

// fac1 and fac2 are the same reference, fac3 is a different reference.
var fac1 = new Facility { ID = "0", Fac_Name = "Hello" };
var fac2 = fac1;
var fac3 = new Facility { ID = "0", Fac_Name = "Hello" };

var facs = new List<Facility>() { fac1, fac2, fac3 };

foreach (var fac in facs.Distinct())
    Console.WriteLine("Id: {0} | Name: {1}", fac.ID, fac.Fac_Name);

// OUTPUT
// Id: 0 | Name: Hello (NOTE: This is the value of fac1/fac2)
// Id: 0 | Name: Hello (This is the value of fac3)

要解决你的困境,你应该:

  • 覆盖Object.GetHashCode()Object.Equals(Object)方法。请注意,Distinct()最终使用GetHashCode()来确定某些内容是否不同,但Equals(Object)GetHashCode()应该一起覆盖。
    Guidelines for Overloading Equals() and Operator ==

    公共类设施 {     公共字符串ID {get;组; }     公共字符串Fac_Name {get;组; }

    // This is just a rough example.
    public override bool Equals(Object obj)
    {
        var fac = obj as Facility;
        if (fac == null) return false;
    
        if (Object.ReferenceEquals(this, fac)) return true;
    
        return (this.ID == fac.ID) && (this.Fac_Name == fac.Fac_Name);
    }
    
    public override int GetHashCode()
    {
        var hash = 13;
    
        if (!String.IsNullOrEmpty(this.ID))
            hash ^= ID.GetHashCode();
        if (!String.IsNullOrEmpty(this.Fac_Name))
            hash ^= Fac_Name.GetHashCode();
    
        return hash;
    }
    

    }



public class FacilityEqualityComparer : IEqualityComparer<Facility>
{
    public bool Equals(Facility x, Facility y)
    {
        return (x.ID == y.ID) && (x.Fac_Name == y.Fac_Name);
    }

    public int GetHashCode(Facility fac)
    {
        var hash = 13;

        if (!String.IsNullOrEmpty(this.ID))
            hash ^= ID.GetHashCode();
        if (!String.IsNullOrEmpty(this.Fac_Name))
            hash ^= Fac_Name.GetHashCode();

        return hash;
    }
}

var facReturnList2 = 
        facReturnList.Select(x => 
            new Facility { ID = x.Fac_Name.Substring(0, 6), 
                  Fac_Name = x.Fac_Name.Substring(0, 3) })
            .Distinct(new FacilityEqualityComparer()).ToList();

另外,还有一些需要注意的事项:

  1. 您的命名不遵循指南。不要在属性名称中使用下划线,ID应为Id。
  2. 无论您决定使用哪种方式,都应该考虑使用String.Equals(...)并指定StringComparison值。我只是在字符串上使用==相等比较来保持帖子简短易读。

答案 1 :(得分:1)

所以问题是Enumerable.Distinct方法使用默认的相等比较器 - 比较哈希码 - 所以无论属性值如何,它都将是一个不同的列表。为该类型构建一个相等比较器:

public class FacilityEqualityComparer : IEqualityComparer<Facility>
{
    public bool Equals(Facility fac1, Facility fac2)
    {
        return fac1.ID.Equals(fac2.ID) && fac1.Fac_Name.Equals(fac2.Fac_Name);
    }

    public int GetHashCode(Facility fac)
    {
        string hCode = fac.ID + fac.Fac_Name;
        return hCode.GetHashCode();
    }
}

然后当你使用它时,请这样称呼:

var facReturnList2 = 
    facReturnList.Select(x => 
        new Facility { ID = x.Fac_Name.Substring(0, 6), 
              Fac_Name = x.Fac_Name.Substring(0, 3) })
        .Distinct(new FacilityEqualityComparer()).ToList();
return facReturnList2;

答案 2 :(得分:1)

Distinct使用默认的相等比较器来检查是否相等。这意味着它正在寻找引用相等,这显然不会出现在你的情况下。

因此,您需要使用自定义IEqualityComparer(请参阅Distinct()的重载,或者您可以使用Distinct()复制GroupBy()的功能First()

facReturnList.Select(x => 
                       new Facility { ID = x.Fac_Name.Substring(0, 6), 
                       Fac_Name = x.Fac_Name.Substring(0, 3) 
                     })
             .GroupBy(x => new{x.ID, x.Fac_Name})
             .Select(y => y.First())
             .ToList();

您还可以在Facility课程中覆盖Equals方法:

public override bool Equals(System.Object obj)
{
    if (ReferenceEquals(null, obj)) return false;
    if (ReferenceEquals(this, obj)) return true;
    if (obj.GetType() != this.GetType()) return false;
    Facility objAsFacility = obj as Facility;
    return Equals(objAsFacility);
}

protected bool Equals(Facility other)
{
    if (other.Fac_Name == this.Fac_Name)
        return true;
    else return false;
}  

public override int GetHashCode()
{
    return this.Fac_Name.GetHashCode(); 
    //Or you might even want to this:
    //return (this.ID + this.Fac_Name).GetHashCode();
}

我可能会使用重写的等于运算符方法。