As usual, I do a lot of thinking. A lot. One of the things I recently noticed is that my base Entity class leaves a lot to be desired.
In specific, it exposes several things: an Id property, overrides for Equals and GetHashCode, and an IsTransient property.
First off, why do I have a property to indicate transience when I shouldn't be concerned with the database?
Second, the use of a single Id property eliminates the possibility for supporting entities that have a natural compound identity. It also isn't very specific. If a variable to an id is found elsewhere in your code as 'id', it doesn't say much about what entity it represents.
Third, if one were to follow the separation of concerns concept closely, they would come to understand that comparisons and validation are their own concerns. How something is compared or validated depends on context does it not? Implementing these in a base class now seems quite wrong to me.
Step One: Eliminate the Entity and ValueObject base classes, adding a more appropriately named identity field(s) to your actual entities. I would much rather set a key in fluent nhibernate using Id(person => person.SSN) rather than Id(person => pesron.Id).
Step Two: Implement IEqualityComparer<T> at different levels as your needs dictate. You may choose to have one comparer for list inclusion and another for one-to-one comparisons, each may look at different attributes on your entities in order to compare them.
Step Three: Write a simple IValidator<T> interface, and implement it in the same manner as your comparers. You can then have different validators at various levels for many different tasks: model validation, entity validation, process validation.
I think the whole upshot here is that we now have a much clearer set of responsibilities and objects that handle them.