3

我想比较记录以查看它们之间是否存在差异。

Person桌子:

ID    Name          Address
--------------------------------
1     John Smith    123 A Street
2     John Smith    123 A Street
3     John Smith    234 B Street

记录 1 和 2 是“相等的”。记录 2 和 3 “不相等”。

IEquatable在模型上实现Person如下。

public static bool operator ==(Person p1, Person p2)
{
    if (System.Object.ReferenceEquals(p1, p2)) return true;

    return p1.Equals(p2);
}

public static bool operator !=(Person p1, Person p2)
{
    return !(p1== p2);
}

public bool Equals(Person other)
{
    if (System.Object.ReferenceEquals(this, other)) return true;

    if (Name != other.Name) return false;
    if (Address != other.Address) return false;

    return true;
}

public override bool Equals(object obj)
{
    Person person = obj as Person;
    if (person == null) return false;

    return Equals(person);
}

public override int GetHashCode()
{
    unchecked
    {
        int hash = (int)2166136261;
        hash = hash * 25165843 ^ (Name != null ? Name .GetHashCode() : 0);
        hash = hash * 25165843 ^ (Address != null ? Address.GetHashCode() : 0);

        return hash;
    }
}

问题是当Persons来自导航属性的 ICollection 被具体化时。它缺少彼此“相等”的记录(即返回单个 John Smith 123 A Street 记录)。我猜这是因为默认情况下它会考虑具有唯一主键的不同实体。通过覆盖 equals 它认为两个记录是同一个实体。

屏幕截图显示Addresses而不是Persons:(顶部有IEquatable,底部没有) 在此处输入图像描述

//Addresses Definition (generated code)
public virtual ICollection<Address> Addresses { get; set; }

我如何协调 EF 需要在对象级别看到平等与我想要看到逻辑平等?

4

3 回答 3

5

关键似乎在EF 源代码中

在 的注释中EntityUtil.DetermineCollectionType(Type requestedType),有这些“规则”:

    // The rules are:
    // If the collection is defined as a concrete type with a publicly accessible parameterless constructor, then create an instance of that type
    // Else, if HashSet{T} can be assigned to the type, then use HashSet{T}
    // Else, if List{T} can be assigned to the type, then use List{T}
    // Else, throw a nice exception.

因此,由此看来,EF 将为HashSet<Address>您的导航属性新增一个。这将使用默认的相等比较器并防止添加任何重复项。由于您的Equals实现将您的两个结果确定为相等,因此将只包括一个。

实体通常是唯一标识的——Equals忽略唯一标识符的覆盖可能是不正确的。最好的解决方案是删除覆盖并实现单独的IEqualityComparer. 大多数使用相等语义的方法都会将此作为参数。

于 2015-01-14T22:24:08.533 回答
2

IEquatable并且IEqualityComparer是 LINQ to 对象所独有的概念。EF 没有实用的方法将任何以这种方式定义的“相等”定义转换为 SQL,因此无法执行此类操作。

要基于某些列获取不同的项目,以一种可以转换为 SQL 的方式,只需根据这些值对项目进行分组,然后从每个组中获取一个项目:

var query = context.Table.GroupBy(row => new
    {
        row.Name,
        row.Address,
    })
    .Select(group => group.FirstOrDefault());
于 2015-01-14T20:56:15.493 回答
0

不要使用IEquatableetc,创建自己的AreEquivalent, orIsEquivalentTo方法。

于 2015-01-14T20:48:22.157 回答