Archive for Software Architecture

NHibernate: Implementing Equals and GetHashCode

Conflicting advice abounds regarding the correct way to implement Equals and GetHashCode for NHibernate entities.

In general, any Equals implementation is required to be:

  • Transitive
  • Reflexive
  • Symmetric

A GetHashCode implementation must have the property that it returns the same value for any two objects for which Equals returns true (it may or may not return the same value for other object pairs).

One approach to determining equality is to compare each and every field between both entities. This approach doesn’t perform particularly well of course and, depending on the implementation, can be difficult to maintain.

I decided to base my Equals/GetHashCode implementation on object identity. All my entity objects are identified by a surrogate GUID that’s lazily initialized on first use. This allows me to implement the functionality in a base entity class and reuse it easily across all entities in the system:

public abstract class NHBEntity
{
    private Guid id;

    public virtual Guid Id
    {
        get
        {
            return (id == Guid.Empty) ? this.id = Guid.NewGuid() : this.id;
        }
        set
        {
            this.id = value;
        }
    }

    public override bool Equals(object obj)
    {
        if (obj == this) return true;

        var rhs = obj as NHBEntity;
        if (rhs == null) return false;

        return (this.Id.Equals(rhs.Id));
    }

    public override int GetHashCode()
    {
        return this.Id.GetHashCode();
    }
}

Being relatively new to NHibernate, I’m half anticipating having to make adjustments but so far this has worked well and, just as importantly, hasn’t been cumbersome to implement.

Comments off

Universally Unique Identifiers (UUIDs) as Surrogate Keys

Universally Unique Identifiers (UUIDs) make good surrogate keys for a number of reasons:

  • They are, by definition, globally unique, leading to practical benefits such as:
    • Given a UUID, it’s possible to identify the relation/tuple (and even potentially the database/environment) for which it is a key. This property can prove useful in the field when troubleshooting.
    • UUIDs will not clash across your environments (unless you want them to).
  • They are decentralised. By that I mean that any system is capable of generating a valid UUID. They can, for example, be generated by the middle-tier.
  • They are (somewhat) obscure – depending on the version of the algorithm used (usually version 4).
  • UUIDs can be generated offline.

 

UNIQUEIDENTIFIER is the SQL Server type for UUIDs. With SQL Server, there are some practical considerations that you need to weigh up for your scenario:

  • UNIQUEIDENTIFIERs require 16 bytes for storage (compared to 4 bytes for an INTEGER).
  • INSERTs will be randomly distributed if the UUID is generated via NEWID(). See NEWSEQUENTIALID() for an alternative.
  • JOINs and other operators involving UNIQUEIDENTIFIERs will likely perform worse compared to those involving INTEGERs.

 

Sometimes, the logical advantages of using UUIDs as generated keys in your system can outweigh these practical considerations.

Comments off