[英]Best practices for Entity Framework entities override Equals and GetHashCode
我想检查两个实体之间是否存在one-to-many
关系的相等性。
所以很明显我覆盖了Object.Equals
方法,但是随后我得到了CS0659编译器警告: 'class' overrides Object.Equals(object o) but does not override Object.GetHashCode()
。
我覆盖了Object.GetHashCode
,但是随后Resharper告诉我, GetHashCode
方法应该在所有对象生命周期中返回相同的结果,并将在可变对象中使用。 ( docs )
public class Computer
{
public long Id { get; set; }
public ICollection<GPU> GPUs { get; set; } = new List<GPU>();
public override bool Equals(object obj)
{
return obj is Computer computer &&
GPUs.All(computer.GPUs.Contains);
}
public override int GetHashCode()
{
return HashCode.Combine(GPUs);
}
}
public class GPU
{
public long Id { get; set; }
public int? Cores { get; set; } = null;
public override bool Equals(object obj)
{
return obj is GPU gpu &&
Cores == gpu.Cores;
}
public override int GetHashCode()
{
return HashCode.Combine(Cores);
}
}
我不知道我该喜欢什么:
Equals
方法而不覆盖GetHashCode
,或者 GetHashCode
吗? 实体框架使用其自己的智能方法来检测对象相等性。 例如,这在调用SaveChanges
:获取的对象的值与更新的对象的值匹配,以检测是否需要SQL更新。
我不确定您的相等性定义是否会与该相等性检查混淆,从而导致某些未更改的项目在数据库中被更新,或更糟糕的是,某些已更改的数据不会在数据库中被更新。
请记住,实体类(放在DbSet<...>
)代表数据库中的表以及这些表之间的关系。
从数据库中提取的两个项目何时应被视为代表同一对象? 它们具有相同的值吗? 我们不能在7月4日在一个数据库中有两个名为“ John Doe”的人吗?
你可以用它来检测两个提取的唯一方法Persons
从数据库中代表相同的Person
是通过检查标识。 某些非主键值不同的事实只会告诉您更改的数据不会在数据库中更新,而不是它是不同的Person
。
我的建议是,使表的表示形式尽可能简单:仅表的列(非虚拟属性)和表之间的关系(虚拟属性)。 没有成员,没有方法,什么都没有。
如果需要其他功能,请创建类的扩展功能。 如果需要非标准的相等比较器,请创建一个单独的相等比较器。 班级的用户可以决定是否要使用默认比较方法还是特殊的比较方法。
这可以与各种String比较器进行比较: StringComparer.OrdinalIgnorCase
, StringComparer.InvariantCulture
等。
回到您的问题
在我看来,您想要一个不检查Id值的Gpu比较器:两个具有不同Id,但其他属性相同的值被认为是相等的。
class GpuComparer : EqualityComparer<Gpu>
{
public static IEqualityComparer<Gpu> IgnoreIdComparer {get;} = new GpuComparer()
public override bool Equals(Gpu x, Gpu y)
{
if (x == null) return y == null; // true if both null, false if x null but y not
if (y == null) return false; // because x not null
if (Object.ReferenceEquals(x, y)) return true;
if (x.GetType() != y.GetType()) return false;
// if here, we know x and y both not null, and of same type.
// compare all properties for equality
return x.Cores == y.Cores;
}
public override int GetHasCode(Gpu x)
{
if (x == null) throw new ArgumentNullException(nameof(x));
// note: I want a different Hash for x.Cores == null than x.Cores == 0!
return (x.Cores.HasValue) ? return x.Cores.Value.GetHashCode() : -78546;
// -78546 is just a value I expect that is not used often as Cores;
}
}
请注意,我添加了针对同一类型的测试,因为如果y是Gpu的派生类,并且您会忽略它们不是同一类型,那么可能是Equals(x,y),而不是Equals(y,x),这是相等函数的前提条件之一
用法:
IEqualityComparer<Gpu> gpuIgnoreIdComparer = GpuComparer.IgnoreIdComparer;
Gpu x = new Gpu {Id = 0, Cores = null}
Gpu y = new Gpu {Id = 1, Cores = null}
bool sameExceptForId = gpuIgnoreIdComparer.Equals(x, y);
x和y将被视为相等
HashSet<Gpu> hashSetIgnoringIds = new HashSet<Gpu>(GpuComparer.IgnoreIdComparer);
hashSetIgnoringIds.Add(x);
bool containsY = hashSetIgnoringIds.Contains(y); // expect true
计算机的比较器将类似。 除了您忘了检查null和类型之外,我在进行相等检查的方式中还遇到其他一些问题:
。
class IgnoreIdComputerComparer : EqualityComparer<Computer>
{
public static IEqualityComparer NoIdComparer {get} = new IgnoreIdComputerCompare();
public override bool (Computer x, Computer y)
{
if (x == null) return y == null;not null
if (y == null) return false;
if (Object.ReferenceEquals(x, y)) return true;
if (x.GetType() != y.GetType()) return false;
// equal if both GPU collections null or empty,
// or any element in X.Gpu is also in Y.Gpu ignoring duplicates
// using the Gpu IgnoreIdComparer
if (x.Gpus == null || x.Gpus.Count == 0)
return y.Gpus == null || y.Gpus.Count == 0;
// equal if same elements, ignoring duplicates:
HashSet<Gpu> xGpus = new HashSet<Gpu>(x, GpuComparer.IgnoreIdComparer);
return xGpush.EqualSet(y);
}
public override int GetHashCode(Computer x)
{
if (x == null) throw new ArgumentNullException(nameof(x));
if (x.Gpus == null || x.Gpus.Count == 0) return -784120;
HashSet<Gpu> xGpus = new HashSet<Gpu>(x, GpuComparer.IgnoreIdComparer);
return xGpus.Sum(gpu => gpu);
}
}
待办事项:如果您要使用大量的GPU,请考虑使用更智能的GetHashCode
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.