Using the Redis NoSql database with .NET Part 10: a higher level of abstraction in the .NET client
April 24, 2017 Leave a comment
Introduction
In the previous post we started looking into a Redis .NET client from ServiceStack. At this point of time there are two recommended .NET clients for Redis with ServiceStack being one and StackExchange.Redis being the other. The single biggest difference between the two is that ServiceStack.Redis requires a paid licence above a certain usage limit. The free-of-charge limit is more than enough for evaluation and testing purposes but you’ll most certainly need to buy a licence for your production environment. Otherwise if your application exceeds the free limits you’ll start to see some exception messages.
ServiceStack.Redis provides three interfaces to communicate with Redis. In the previous post we looked at the most basic one, i.e. IRedisNativeClient. It provides a wide range of low level database operations.
Most of these operations map to Redis commands like GET, SET, SMEMBERS, ZADD etc. one to one. However, the methods require string and byte array inputs and the programmer is responsible for all data conversion back and forth which results in a lot of code to achieve simple stuff.
In this post we’ll go slightly higher with the IRedisClient interface. We’ll also see how to get hold of a Redis client from Redis client manager.
The IRedisClient
The IRedisClient provides a more pleasant interface to communicate with the Redis database. Here’s an example of setting and getting a string value. The example also shows how to set multiple key-value pairs:
private static void TryRedisClient() { IRedisClientsManager clientManager = new BasicRedisClientManager(); string customerOneNameKey = "customer:1:name"; using (IRedisClient redisClient = clientManager.GetClient()) { redisClient.SetValue(customerOneNameKey, "Great Customer"); redisClient.SetValues(new Dictionary<string, string>() { { "someKey", "someValue" }, { "someOtherKey", "someOtherValue" } }); } using (IRedisClient redisClient = clientManager.GetClient()) { string name = redisClient.GetValue(customerOneNameKey); Console.WriteLine(name); } }
A great improvement compared to the IRedisNativeClient interface is that we can forget the byte array arguments and return values. We can also see how to get hold of a Redis client from the BasicRedisClientManager object. BasicRedisClientManager doesn’t provide connection pooling. It creates a new client each time it is called. Connection pooling is provided by the PooledRedisClientManager object which “imposes a maximum connection limit and when its maximum pool size has been reached will instead block on any new connection requests until the next RedisClient is released back into the pool. If no client became available within PoolTimeout, a Pool TimeoutException will be thrown.” (via the documentation available here).
The client manager objects have a number of overloads to provide a host name, a port number and other parameters.
The following example shows how the Redis client exposes lists, sets and hashes via aptly named properties. There’s a property for sorted sets as well. These properties enable us to work with these data types almost as if they are native .NET lists, hash sets and dictionaries:
private static void TryVariousRedisClientCommands() { IRedisClientsManager pooledClientManager = new PooledRedisClientManager(0, "127.0.0.1:6379"); string setId = "colours"; string listId = "to-do"; string hashId = "customer:2"; using (IRedisClient pooledClient = pooledClientManager.GetClient()) { pooledClient.Sets[setId].Add("green"); pooledClient.Sets[setId].Add("blue"); var setItems = pooledClient.Sets[setId].GetAll(); Console.WriteLine(string.Join("|", setItems)); var toDoList = pooledClient.Lists[listId]; toDoList.Clear(); toDoList.Add("watch tv"); toDoList.Add("sleep"); toDoList.Add("write blog"); toDoList.Add("play with kids"); var listItems = pooledClient.Lists[listId].GetAll(); Console.WriteLine(string.Join("|", listItems)); string nextToDo = pooledClient.Lists[listId].Pop(); Console.WriteLine(nextToDo); pooledClient.Hashes[hashId]["name"] = "New Customer"; pooledClient.Hashes[hashId]["id"] = "2"; pooledClient.Hashes[hashId]["address"] = "Skopje, Macedonia"; foreach (var kvp in pooledClient.Hashes[hashId]) { Console.WriteLine(string.Concat(kvp.Key, ": ", kvp.Value)); } } }
PooledRedisClientManager has 8 overloads to provide various settings. The one in the example accepts an integer and an array of connection strings. The array is necessary in case we have a cluster of Redis servers. We provided the default connection string for demonstration purposes. The integer indicates the database number which also defaults to 0. We haven’t talked about databases in Redis yet, we’ll do that in a later post. For now it’s enough to know that databases are numbered in Redis and the numbering starts from 0. The maximum number of databases is configurable and is normally set to 16 to begin with.
The example should be easy to follow. Note how the Sets, Lists and Hashes properties are used to work with those data types. The code generates the following output:
blue|green|yellow
watch tv|sleep|write blog|play with kids
play with kids
name: New Customer
id: 2
address: Skopje, Macedonia
This client is much more convenient to work with. It hides some of the tedious low-level communication steps behind the scenes like converting the strings to jagged byte arrays. However, we still cannot work with our custom objects in an easy way. We’ll look at the IRedisTypedClient interface in the next post which solves this requirement.
You can view all posts related to data storage on this blog here.