Tuesday, 8 April 2014

ServiceStack.Redis and StructureMap

I've tripped up on this a couple of times so I've just decided to layout the right way to make use of the ServiceStack.Redis client library when using StructureMap DI Container. The mistake I used to make was to have my controller or service depend upon an IRedisClient. StructureMap would be configured to provide an instance of an IRedisClient by invoking GetClient() on the whatever singleton instance of IRedisClientsManager I was using, usually the PooledRedisClientsManager. Much was learned from this SO answer.

This was a problem because it limited the re-usability of the consuming controller/service. Such a service might provide a method that used the injected IRedisClient to interact with the Redis instance. The IRedisClient implements IDisposable and should be wrapped in a using statement in order to release the resource and close the channel when it is has completed the task. In doing so, you make the method on the service non-idempotent: it has the side-effect of changing the state of the service by disposing of its injected IRedisClient instance. You are only able to successfully call the service method once, calling it a second time throws an exception.

The solution is fairly simple. Instead of having the service depend directly on the IRedisClient, the service instead needs to depend on the IRedisClientsManager. This then allows the service to call the GetClient() method on the IRedisClientsManager itself and create an instance of IRedisClient every time the method is called. Idempotency is achieved because a client of the service can call the method any number of times and not cause any unintended side-effects. The state of the service is no longer affected by disposing of its only instance of IRedisClient. The IRedisClient instances that are created are still disposed of, but the service has the ability to create a new one for itself.

The pattern is very similar to the RavenDB IDocumentStore and IDocumentSession that I am familiar with, so I surprised it took me so long to figure out!