Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>That's an interesting approach but instead of taking the time to understand and critique I will just offer my solution to this problem.</p> <p>I don't like the idea of a generic entity base class, so my solution only supports int, Guid and string identities. Some of the code below, such as using a <code>Func&lt;int&gt;</code> to get the hash code, only exists to support case-insensitive string comparisons. If I ignored string identifiers (and I wish I could), the code would be more compact.</p> <p>This code passes the unit tests I have for it and hasn't let me down in our applications but I'm sure there are edge cases. The only one I've thought of is: If I new and save an entity it will keep its original hash code, but if after the save I retrieve an instance of the same entity from the database in another session it will have a different hash code.</p> <p>Feedback welcome.</p> <p>Base class:</p> <pre><code>[Serializable] public abstract class Entity { protected int? _cachedHashCode; public abstract bool IsTransient { get; } // Check equality by comparing transient state or id. protected bool EntityEquals(Entity other, Func&lt;bool&gt; idEquals) { if (other == null) { return false; } if (IsTransient ^ other.IsTransient) { return false; } if (IsTransient &amp;&amp; other.IsTransient) { return ReferenceEquals(this, other); } return idEquals.Invoke(); } // Use cached hash code to ensure that hash code does not change when id is assigned. protected int GetHashCode(Func&lt;int&gt; idHashCode) { if (!_cachedHashCode.HasValue) { _cachedHashCode = IsTransient ? base.GetHashCode() : idHashCode.Invoke(); } return _cachedHashCode.Value; } } </code></pre> <p>int identity:</p> <pre><code>[Serializable] public abstract class EntityIdentifiedByInt : Entity { public abstract int Id { get; } public override bool IsTransient { get { return Id == 0; } } public override bool Equals(object obj) { if (obj == null || obj.GetType() != GetType()) { return false; } var other = (EntityIdentifiedByInt)obj; return Equals(other); } public virtual bool Equals(EntityIdentifiedByInt other) { return EntityEquals(other, () =&gt; Id == other.Id); } public override int GetHashCode() { return GetHashCode(() =&gt; Id); } } </code></pre> <p>Guid identity:</p> <pre><code>[Serializable] public abstract class EntityIdentifiedByGuid : Entity { public abstract Guid Id { get; } public override bool IsTransient { get { return Id == Guid.Empty; } } public override bool Equals(object obj) { if (obj == null || obj.GetType() != GetType()) { return false; } var other = (EntityIdentifiedByGuid)obj; return Equals(other); } public virtual bool Equals(EntityIdentifiedByGuid other) { return EntityEquals(other, () =&gt; Id == other.Id); } public override int GetHashCode() { return GetHashCode(() =&gt; Id.GetHashCode()); } } </code></pre> <p>string identity:</p> <pre><code>[Serializable] public abstract class EntityIdentifiedByString : Entity { public abstract string Id { get; } public override bool IsTransient { get { return Id == null; } } public override bool Equals(object obj) { if (obj == null || obj.GetType() != GetType()) { return false; } var other = (EntityIdentifiedByString)obj; return Equals(other); } public virtual bool Equals(EntityIdentifiedByString other) { Func&lt;bool&gt; idEquals = () =&gt; string.Equals(Id, other.Id, StringComparison.OrdinalIgnoreCase); return EntityEquals(other, idEquals); } public override int GetHashCode() { return GetHashCode(() =&gt; Id.ToUpperInvariant().GetHashCode()); } } </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload