IEquatable 中断 Entity Framework 个实体的加载
IEquatable breaks loading of Entity Framework entities
我想比较记录,看看它们之间是否有差异。
Person
table:
ID Name Address
--------------------------------
1 John Smith 123 A Street
2 John Smith 123 A Street
3 John Smith 234 B Street
记录 1 和 2 为 "equal"。记录 2 和 3 是 "not equal".
我在模型 Person
上实现了 IEquatable
,如下所示。
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 被具体化时。它缺少彼此的记录 "equal"(即返回单个 John Smith 123 A Street 记录)。我猜这是因为默认情况下它会考虑具有唯一主键的不同实体。通过覆盖 equals,它认为两个记录是同一个实体。
显示 Addresses
而不是 Persons
的屏幕截图:(顶部有 IEquatable
,底部没有)
//Addresses Definition (generated code)
public virtual ICollection<Address> Addresses { get; set; }
如何协调 EF 需要在对象级别看到相等性与我希望看到逻辑相等性?
不要使用 IEquatable
等,创建您自己的 AreEquivalent
或 IsEquivalentTo
方法。
IEquatable
和 IEqualityComparer
是 LINQ to objects 几乎独有的概念。 EF 无法将以这种方式定义的 "equality" 的任何定义转换为 SQL,因此无法执行此类操作。
要根据某些列获取不同的项目,以一种可以转化为 SQL 的方式,只需根据这些值对项目进行分组,然后从每组中获取一个项目:
var query = context.Table.GroupBy(row => new
{
row.Name,
row.Address,
})
.Select(group => group.FirstOrDefault());
钥匙好像在EF source code
在 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
。大多数使用相等语义的方法都会将其作为参数。
我想比较记录,看看它们之间是否有差异。
Person
table:
ID Name Address
--------------------------------
1 John Smith 123 A Street
2 John Smith 123 A Street
3 John Smith 234 B Street
记录 1 和 2 为 "equal"。记录 2 和 3 是 "not equal".
我在模型 Person
上实现了 IEquatable
,如下所示。
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 被具体化时。它缺少彼此的记录 "equal"(即返回单个 John Smith 123 A Street 记录)。我猜这是因为默认情况下它会考虑具有唯一主键的不同实体。通过覆盖 equals,它认为两个记录是同一个实体。
显示 Addresses
而不是 Persons
的屏幕截图:(顶部有 IEquatable
,底部没有)
//Addresses Definition (generated code)
public virtual ICollection<Address> Addresses { get; set; }
如何协调 EF 需要在对象级别看到相等性与我希望看到逻辑相等性?
不要使用 IEquatable
等,创建您自己的 AreEquivalent
或 IsEquivalentTo
方法。
IEquatable
和 IEqualityComparer
是 LINQ to objects 几乎独有的概念。 EF 无法将以这种方式定义的 "equality" 的任何定义转换为 SQL,因此无法执行此类操作。
要根据某些列获取不同的项目,以一种可以转化为 SQL 的方式,只需根据这些值对项目进行分组,然后从每组中获取一个项目:
var query = context.Table.GroupBy(row => new
{
row.Name,
row.Address,
})
.Select(group => group.FirstOrDefault());
钥匙好像在EF source code
在 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
。大多数使用相等语义的方法都会将其作为参数。