Add a null caching engine.

Having a NullEngine makes a bunch of internal operations simpler as we don't have to check whether or not the engine() method returned an instance or not. It also makes it easy to use a null adapter in testing without having to generate mocks.

Fixes #4281