Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DDD: GetHashCode and primary id

I have seen DDD Domain implementations where entities rely on the primary key ID when building Equals/GetHashCode methods. I understand why it is a good idea, as the primary key might be the only member which is not mutable. But there might also be situations where it is no good idea, though.

Think about a Dictionary, holding entities which where just instantiated. The primary key (assume it is an auto-incrementing value, not a "business-related" value) is not assigned yet, it might be 0 on each entity. Now, the entities within the Dictionary will be saved. This implies a change of the primary key resulting in a different hash code.

My question: what pattern should be used regarding GetHashCode if no business-related primary key is available and all members are mutable?

Thank you in advance.

like image 839
mbue Avatar asked May 20 '26 22:05

mbue


1 Answers

In such cases, I only rely on the Id in the Equals and GetHashcode methods, if the Id has been assigned. In other cases, I compare for equality using the reference. The same goes for the GetHashCode implementation; I only create a hashcode based on the 'Id', if the Id has been assigned. (That is, if the entity is not transient).

The code below, is what I use. This is somewhat based on the entities in Sharp architecture:

        public override bool Equals( object obj )
        {
            Entity<TId> other = obj as Entity<TId>;

            if( other == null || this.GetType() != other.GetType() )
            {
                return false;
            }

            bool otherIsTransient = Equals (other.Id, default(TId));
            bool thisIsTransient = Equals (this.Id, default (TId));

            if( otherIsTransient && thisIsTransient )
            {
                return ReferenceEquals (this, other);
            }

            return Id.Equals (other.Id);

        }

First, I check whether both entities are of the same type. (Actually, you could create a typed version of this method as well, by implementing the correct interface (IEquality<T>) When both entities are of the same type, I check whether one of them is transient. I just do it by checking their Id property: if it contains the default value, it means that they haven't been saved in the DB yet. If both of them are transient, I compare both entities on their reference. Otherwise, I can compare them on their ID.

The GetHashCode method that I use, makes sure that the value that's being returned, never changes:

        private int? _oldHashCode;

        public override int GetHashCode()
        {
            // Once we have a hash code we'll never change it
            if( _oldHashCode.HasValue )
            {
                return _oldHashCode.Value;
            }

            bool thisIsTransient = Equals (Id, default(TId));


            // When this instance is transient, we use the base GetHashCode()
            // and remember it, so an instance can NEVER change its hash code.

            if( thisIsTransient )
            {
                _oldHashCode = base.GetHashCode ();

                return _oldHashCode.Value;
            }

            return Id.GetHashCode ();
        }
like image 55
Frederik Gheysels Avatar answered May 22 '26 12:05

Frederik Gheysels



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!