Strongly-typed database ids

More adventures in strongly-typed database ID fields. A follow-up (of sorts) to a post from 2013.

In that previous post I described a way of adding strong typing to database ID references in C# code, without really any runtime overhead, and interoperability with existing code which  passes database IDs as integers. This post presents a refinement which is more flexible, and produces less cluttered code.

Background

In a lot of database-heavy apps, at least the ones I’ve been involved in, you spend a lot of time passing database IDs around in the code. Usually these are integers (32- or 64-bit), but they could also be UUIDs or strings.

The trouble is that an integer representing a customer ID has the same static type as an integer representing a user ID, invoice line ID, product ID, or for that matter an integer representing a quantity. The compiler will not complain at you when you pass an integer representing a user ID to a function expecting an integer representing a customer ID—because they are all just undifferentiated integers.

So it’s an appealing idea to somehow introduce static type checking for database entity IDs. Of course, we should avoid bloating the code or introducing any runtime overhead and it should easily interoperate with whatever the native key type is for the database entities.

Ideally the scheme should even cope with composite primary keys, though in my experience composite primary keys are pretty rare (at least when using an ORM which doesn’t directly expose joining tables).

Previous approach

In ID: Type-safety in database code, I described a C# generic struct type, ID<> for representing strongly-typed database IDs. It worked, but had the following shortcomings:

  • It was verbose: ID types look like ID<Customer> or ID<Invoice>, which is awkward to type and visually messy.
  • It was limited: It assumed that database IDs are always 32-bit integers. Different types of keys—for example, some tables with string keys and others with integers— cannot be mixed in a single project without creating multiple, different-named ID classes.

On the positive side:

  • IDs were ‘struct’ objects, and hence caused zero space overhead and minimal speed overhead.

New approach

Ideally key types would be named EntityName.Id, but how can we do that while keeping them as structs, and without requiring each entity to redefine its own Id struct?

The answer is to make it an inner type of a parameterised Entity class (parameterised by database key type and Entity subtype). Subclasses instantiate the parameter types, and get an Id struct type strongly typed with respect to their key and Entity type.

  • ID types now look like Customer.ID or Invoice.ID—which is visually less noisy, and puts the entity name first.
  • ID (entity key) types can be anything—Int32, Int64, String, anything—which implements IEquatable.
  • Entities have an ‘Id’ property which is of type EntityName.ID.
  • Entities have a ‘Key’ property which is of the underlying primary key type.

The downside is that all entity classes must inherit from the same Entity<> base class in order to be able to have-strongly typed ID types. However, since the entity ‘knows’ about its ID type, it can expose an Id field of that type.

It’s possible for many entities which share the same underlying key type (and key field name) to inherit from a common subclass of Entity, specialised to their key type.

The Code

// base class of all entities:
public abstract class Entity<K, E>: Entity<K, E>.IDOrEntity
	where E: Entity<K, E>
	where K: IComparable
{
	public ID Id => new ID(Key);

        // Subclasses must implement this:
	public abstract K Key { get; set; }

	public bool IsNot(Entity<K, E> other) => !Is(other);

	public bool Is(Entity<K, E> other) => this.Id == other.Id;

	// Union type of Entity and ID
	public interface IDOrEntity
	{
		ID Id { get; }

		K Key { get; }
	}

	// The ID type, unique to the Entity type:
	public struct ID: IEquatable, IDOrEntity
	{
		private readonly K _key;

		public ID(K key)
		{
			this._key = key;
		}

		public K Key => _key;

		public ID IDOrEntity.Id => this;

		public override bool Equals(object obj) => this == (obj as ID?);

		public static bool operator !=(ID first, ID second) => !(first == second);

		public static bool operator ==(ID first, ID second) => first.Equals(second);

		public bool Equals(ID other) => this.Key.Equals(other.Key);

		public override int GetHashCode() => Key.GetHashCode();

		public override string ToString() => Key.ToString();

		public static implicit operator ID(K value) => new ID(value);
	}
}

You’ll notice that there is one abstract property on Entity: Key; this represents the entity’s (primary) key as its underlying type. Making this abstract allows subclasses to decide how they want to store all their fields—the Entity class itself does not store any state.

(It might be possible to make this Key field protected.)

Examples

var cust = new Customer();
var cust1 = new Customer();
var custId = (Customer.ID)89;
var order = new Order();
public bool CheckCustomer(Customer.ID id);

var ok = CheckCustomer(cust.id);
// var doesNotCompiler = CheckCustomer(order.id);
var idsMatch = custId == cust1.id;
// var doesNotCompile = custId == order.Id;
var sameEntity = cust.Is(cust1); // Compares Id values for equality.
Customer.ID custId = 33; // Allowed (if key type is integer).
// Customer.ID custId2 = order.Id // Not allowed (no matter if they share key types).

Entities which use the same key type/key name

I have a few line-of-business applications most of which use 32-bit integer entity IDs. Table key names are almost always ‘Id’, and they use Microsoft’s EntityFramework for database access. We can abstract the common bits of the 32-bit-ID database entities like this:

// All (or most) entities in the application inherit (directly) from this:
public abstract class BaseEntity<E> : Entity<int, E>
	where E: BaseEntity<E>
{
	[Key("Id")]
	public override int Key { get; set; }
}

This says that all inheriting entities have an Int32 key field (and hence an ID type based on ints), represented in the database as a field called ‘Id‘.

Accepting IDs or entities

As with my previous approach, it includes a mechanism for methods to receive as parameters objects which can be either an ID or a whole entity.

This is useful because frequently business logic already has an entity object, and it’s a useful optimisation for called methods not to have to retrieve the same entity again from the database.

We specify an interface to represent the union of an Entity type and its corresponding ID type, called EntityName.IDOrEntity. Entities and their IDs implement this interface, and an extension method on the interface, GetEntity(Func<ID, Entity>), provides a mechanism to either return the entity, or to look up the entity from its ID.

In other words, if you provide an entity, the method can use it directly; if you provide just an ID, it can look up the entity itself.

public static class IDOrEntityExtensions
{
	public static EntityType GetInstance<K, EntityType>(
		this Entity<K, EntityType>.IDOrEntity idOrEntity,
		Func<Entity<K, EntityType>.ID, EntityType> getter)
		where EntityType : Entity<K, EntityType>
		where K : IComparable
	{
		var instanceOrNull = idOrEntity as EntityType;
		return instanceOrNull != null
			? instanceOrNull
			: getter(idOrEntity.Id);
	}
}

Summary

It’s only a single class (plus one extension class), but it provides a nice (and simple) mechanism for enforcing a bit more compile-time safety on a database application.

The new version is more intuitive too and makes the code clearer and cleaner.

Non-nullable reference types in C# & .NET

Note: This article/proposal is an aggregation of several previous blog posts written over the past few years. The content is mostly the same, but has been edited for consistency and clarity.

Abstract

In the C# programming language, there are two kinds of data types: value types and reference types. Reference types can hold either pointers to objects, or the special value null, which is used to indicate a ‘missing’ reference. This feature of implicitly allowing nulls in reference types has some well-recognised problems and the .NET team are already exploring ways of enforcing non-nullable reference types; this paper explores one possible design. Continue reading

Nullable reference types in C#: backwards compatability

C# Non-Nullable Types: Backwards Compatibility

In previous episodes I proposed a set of requirements for non-nullable reference types in C#/.NET, and a proposed design, including their interaction with generic types (polymorphic types).

There is also something to be said about backwards- and forwards- compatibility:

  1. How can legacy code — using implicitly-nullable reference types — be migrated to using non-nullable references without causing disruption?
  2. How should legacy code interact with APIs which accept or return non-nullable reference types?
  3. What are the implications of generic code to backwards compatibility?

Continue reading

Ionic mobile development, growing pains

I’ve been developing a mobile app with Ionic 2/Angular 2/Cordova/TypeScript, and given how many moving parts there are in there, and that Ionic 2 and Angular 2 were still beta software when I started—and that I was completely new to all of the technologies when I started—it’s gone relatively well.

However, it’s not a totally smooth development experience, and there are lots of little annoyances which occasionally turn it from pleasant to hideously frustrating.

Continue reading