Entity Classes, IDs and Equality
I want my entity classes to rely on their ID when checking equality, the ID is populated from the repository when I retrieve an object and automatically generated when I save a new entity.
This poses the problem of how I compare two entities before they have been saved while working with them in my domain, or, when I have ‘newed’ them up myself for unit testing. The answer is to use a transient ID.
Normally an Entity Base class might look like this:
public abstract class EntityBase : IEquatable<EntityBase> {public virtual int Id { get; protected set; }public override bool Equals(object other) {return Equals(other as EntityBase);}public override int GetHashCode() {return Id;}public virtual bool Equals(EntityBase other) {if (other == null) return false;return other.Id == Id && other.GetType() == GetType();}}
If two new objects are created from a derived class then object1.Equals(object2) will be true as Id will be 0 for both objects. To avoid this I need to check if an object has not yet been saved:
private bool IsTransient {get { return Id == 0; }}
And just check for referential equality if this is the case:
public virtual bool Equals(EntityBase other) {if (other == null) return false;if (IsTransient) return ReferenceEquals(this, other);return other.Id == Id && other.GetType() == GetType();}
GetHashCode() needs to be overridden to reflect this:
public override int GetHashCode() {if (IsTransient) return base.GetHashCode();return Id;}
The full code for EntityBase is:
public abstract class EntityBase : IEquatable<EntityBase> {public virtual int Id { get; protected set; }private bool IsTransient {get { return Id == 0; }}public override bool Equals(object other) {return Equals(other as EntityBase);}public override int GetHashCode() {if (IsTransient) return base.GetHashCode();return Id;}public virtual bool Equals(EntityBase other) {if (other == null) return false;if (IsTransient) return ReferenceEquals(this, other);return other.Id == Id && other.GetType() == GetType();}}