I want to check equality between two entities with one-to-many
relationships inside them.
So obviously I overrode the Object.Equals
method, but then I get the CS0659 compiler warning: 'class' overrides Object.Equals(object o) but does not override Object.GetHashCode()
.
I overrode the Object.GetHashCode
, but then, Resharper told me that the GetHashCode
method should return the same result for all object life cycle, and will used in mutable objects. (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);
}
}
I don't know what should I prefer:
- Overriding the
Equals
method without overridingGetHashCode
, or - Overriding the
GetHashCode
with immutable data?
Entity Framework uses its own smart methods to detect object equality. This is for instance used if you call
SaveChanges
: the values of fetched objects are matched with the values of updated objects to detect whether a SQL update is needed or not.I'm not sure whether your definitions of equality would mess with this equality checking, causing some unchanged items to be updated in the database, or even worse, some changed data not to be updated in the database.
Database equality
Keep in mind that your entity classes (the classes that you put in the
DbSet<...>
) represent the tables in your database and the relations between the tables.When should two items extracted from your database considered to represent the same object? Is it when they have same values? Can't we have two Persons named "John Doe", born on the 4th of July in one database?
The only way you can use to detect that two extracted
Persons
from the database represent the samePerson
is by checking the Id. The fact that some non-primary key values differ only tells you that the changed data is not updated in the database, not that it is a differentPerson
.Override Equals vs Create EqualityComparer
My advice would be, to keep your table representations as simple as possible: only the columns of the table (non-virtual properties) and the relations between the tables (virtual properties). No members, no Methods, nothing.
If you need extra functionality, create extension functions of the classes. If you need non-standard equality comparison methods, create a separate equality comparer. Users of your class can decide whether they want to use the default comparison method or your special comparison method.
This is all comparable as the various kinds of String Comparers:
StringComparer.OrdinalIgnorCase
,StringComparer.InvariantCulture
, etc.Back to your question
It seems to me that you want a Gpu comparer that does not check the value of Id: two items that have different Id, but same values for other properties are considered equal.
Note that I added the test for same type, because if y is a derived class of Gpu, and you would ignore that they are not the same type, then maybe Equals(x, y), but not Equals(y, x), which is one of the prerequisites of equality functions
Usage:
x and y will be considered equal
A comparer for Computer will be similar. Apart that you forgot to check for null and types, I see some other problems in the way you want to do the equality checking:
.
TODO: if you will be using large collections of Gpus, consider a smarter GetHashCode