Using ServiceStack with Redis in C#

This guide covers the fundamentals of integrating Redis with C# applications using the ServiceStack.Redis client library. While earlier articles explored Redis basics like persistence and virtual memory configuraton, this piece focuses on practical application development using C# to interact with Redis servers.

Installing ServiceStack

ServiceStack.Redis can be installed through NuGet package manager. After installation, four DLLs are added to the project, providing the core functionality needed for Redis operations.

Setting Up Redis Servers

For this implementation, a master-slave Redis configuration is used. The master server runs on port 6379, while the slave server operates on port 6380. This setup demonstrates read/write separation capabilities.

Creating Helper Classes

Several helper class are created to encapsulate Redis operations. These classes provide a clean abstraction layer over the raw Redis commands.

Configuration Class

The configuration class manages server connection parameters including read/write server addresses and pool sizes.

using System;
using System.Configuration;

namespace RedisUtility
{
    public sealed class ServerConfiguration : ConfigurationSection
    {
        public static string WriteServerAddresses
        {
            get
            {
                return string.Format("{0},{1}", "127.0.0.1:6379", "127.0.0.1:6380");
            }
        }

        public static string ReadServerAddresses
        {
            get
            {
                return string.Format("{0}", "127.0.0.1:6379");
            }
        }

        public static int MaxWritePoolSize
        {
            get { return 50; }
        }

        public static int MaxReadPoolSize
        {
            get { return 200; }
        }

        public static bool AutoStart
        {
            get { return true; }
        }
    }
}

Connection Manager

The manager class handles the pooled Redis client connections, ensuring efficient resource management.

using ServiceStack.Redis;
using System;

namespace RedisUtility
{
    public class ConnectionManager
    {
        private static PooledRedisClientManager _clientManager;

        static ConnectionManager()
        {
            Initialize();
        }

        private static void Initialize()
        {
            string[] writeServers = SplitAddresses(ServerConfiguration.WriteServerAddresses);
            string[] readServers = SplitAddresses(ServerConfiguration.ReadServerAddresses);

            _clientManager = new PooledRedisClientManager(
                readServers,
                writeServers,
                new RedisClientManagerConfig
                {
                    MaxWritePoolSize = ServerConfiguration.MaxWritePoolSize,
                    MaxReadPoolSize = ServerConfiguration.MaxReadPoolSize,
                    AutoStart = ServerConfiguration.AutoStart,
                });
        }

        private static string[] SplitAddresses(string addressString)
        {
            return addressString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
        }

        public static IRedisClient GetClient()
        {
            if (_clientManager == null)
                Initialize();
            return _clientManager.GetClient();
        }
    }
}

Base Operation Class

The base class provides common functionality for all Redis data type operations and implements proper resource disposal.

using ServiceStack.Redis;
using System;

namespace RedisUtility
{
    public abstract class RedisBase : IDisposable
    {
        public static IRedisClient Core { get; private set; }
        private bool _disposed = false;

        static RedisBase()
        {
            Core = ConnectionManager.GetClient();
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!this._disposed)
            {
                if (disposing)
                {
                    Core.Dispose();
                    Core = null;
                }
            }
            this._disposed = true;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        public void Persist()
        {
            Core.Save();
        }

        public void PersistAsync()
        {
            Core.SaveAsync();
        }
    }
}

String Operations

String operations cover basic key-value storage, appending, increment/decrement, and retrieval.

using System;
using System.Collections.Generic;

namespace RedisUtility
{
    public class StringOperations : RedisBase
    {
        public static bool SetValue(string key, string value)
        {
            return RedisBase.Core.Set<string>(key, value);
        }

        public static bool SetValue(string key, string value, DateTime expiration)
        {
            return RedisBase.Core.Set<string>(key, value, expiration);
        }

        public static bool SetValue(string key, string value, TimeSpan expiration)
        {
            return RedisBase.Core.Set<string>(key, value, expiration);
        }

        public static void SetMultiple(Dictionary<string, string> pairs)
        {
            RedisBase.Core.SetAll(pairs);
        }

        public static long AppendValue(string key, string value)
        {
            return RedisBase.Core.AppendToValue(key, value);
        }

        public static string GetValue(string key)
        {
            return RedisBase.Core.GetValue(key);
        }

        public static List<string> GetValues(List<string> keys)
        {
            return RedisBase.Core.GetValues(keys);
        }

        public static List<T> GetValues<T>(List<string> keys)
        {
            return RedisBase.Core.GetValues<T>(keys);
        }

        public static string GetAndSetValue(string key, string value)
        {
            return RedisBase.Core.GetAndSetValue(key, value);
        }

        public static long GetValueLength(string key)
        {
            return RedisBase.Core.GetStringCount(key);
        }

        public static long Increment(string key)
        {
            return RedisBase.Core.IncrementValue(key);
        }

        public static double IncrementBy(string key, double amount)
        {
            return RedisBase.Core.IncrementValueBy(key, amount);
        }

        public static long Decrement(string key)
        {
            return RedisBase.Core.DecrementValue(key);
        }

        public static long DecrementBy(string key, long amount)
        {
            return RedisBase.Core.DecrementValueBy(key, amount);
        }
    }
}

List Operations

List operations support push, pop, blocking operations, and range queries from both ends of the list.

using ServiceStack.Redis;
using System;
using System.Collections.Generic;

namespace RedisUtility
{
    public class ListOperations : RedisBase
    {
        public static void PushLeft(string key, string value)
        {
            RedisBase.Core.PushItemToList(key, value);
        }

        public static void PushLeft(string key, string value, DateTime expiration)
        {
            RedisBase.Core.PushItemToList(key, value);
            RedisBase.Core.ExpireEntryAt(key, expiration);
        }

        public static void PushLeft(string key, string value, TimeSpan expiration)
        {
            RedisBase.Core.PushItemToList(key, value);
            RedisBase.Core.ExpireEntryIn(key, expiration);
        }

        public static void PushRight(string key, string value)
        {
            RedisBase.Core.PrependItemToList(key, value);
        }

        public static void PushRight(string key, string value, DateTime expiration)
        {
            RedisBase.Core.PrependItemToList(key, value);
            RedisBase.Core.ExpireEntryAt(key, expiration);
        }

        public static void PushRight(string key, string value, TimeSpan expiration)
        {
            RedisBase.Core.PrependItemToList(key, value);
            RedisBase.Core.ExpireEntryIn(key, expiration);
        }

        public static void AddItem(string key, string value)
        {
            RedisBase.Core.AddItemToList(key, value);
        }

        public static void AddItem(string key, string value, DateTime expiration)
        {
            RedisBase.Core.AddItemToList(key, value);
            RedisBase.Core.ExpireEntryAt(key, expiration);
        }

        public static void AddItems(string key, List<string> values)
        {
            RedisBase.Core.AddRangeToList(key, values);
        }

        public static long GetCount(string key)
        {
            return RedisBase.Core.GetListCount(key);
        }

        public static List<string> GetAll(string key)
        {
            return RedisBase.Core.GetAllItemsFromList(key);
        }

        public static List<string> GetRange(string key, int start, int end)
        {
            return RedisBase.Core.GetRangeFromList(key, start, end);
        }

        public static string BlockingPop(string key, TimeSpan? timeout)
        {
            return RedisBase.Core.BlockingDequeueItemFromList(key, timeout);
        }

        public static ItemRef BlockingPopMultiple(string[] keys, TimeSpan? timeout)
        {
            return RedisBase.Core.BlockingPopItemFromLists(keys, timeout);
        }

        public static string PopItem(string key)
        {
            return RedisBase.Core.PopItemFromList(key);
        }

        public static long RemoveItem(string key, string value)
        {
            return RedisBase.Core.RemoveItemFromList(key, value);
        }

        public static string RemoveLast(string key)
        {
            return RedisBase.Core.RemoveEndFromList(key);
        }

        public static string RemoveFirst(string key)
        {
            return RedisBase.Core.RemoveStartFromList(key);
        }

        public static string MoveBetweenLists(string sourceKey, string destinationKey)
        {
            return RedisBase.Core.PopAndPushItemBetweenLists(sourceKey, destinationKey);
        }
    }
}

Hash Operations

Hash operations provide field-value pair management within hash data structures.

using System;
using System.Collections.Generic;

namespace RedisUtility
{
    public class HashOperations : RedisBase
    {
        public static bool SetField(string hashKey, string field, string value)
        {
            return RedisBase.Core.SetEntryInHash(hashKey, field, value);
        }

        public static bool SetFieldIfNotExists(string hashKey, string field, string value)
        {
            return RedisBase.Core.SetEntryInHashIfNotExists(hashKey, field, value);
        }

        public static void StoreObject<T>(T entity) where T : class
        {
            RedisBase.Core.StoreAsHash<T>(entity);
        }

        public static T GetObject<T>(object id) where T : class
        {
            return RedisBase.Core.GetFromHash<T>(id);
        }

        public static Dictionary<string, string> GetAllEntries(string hashKey)
        {
            return RedisBase.Core.GetAllEntriesFromHash(hashKey);
        }

        public static long GetFieldCount(string hashKey)
        {
            return RedisBase.Core.GetHashCount(hashKey);
        }

        public static List<string> GetAllKeys(string hashKey)
        {
            return RedisBase.Core.GetHashKeys(hashKey);
        }

        public static List<string> GetAllValues(string hashKey)
        {
            return RedisBase.Core.GetHashValues(hashKey);
        }

        public static string GetFieldValue(string hashKey, string field)
        {
            return RedisBase.Core.GetValueFromHash(hashKey, field);
        }

        public static List<string> GetFieldValues(string hashKey, string[] fields)
        {
            return RedisBase.Core.GetValuesFromHash(hashKey, fields);
        }

        public static bool DeleteField(string hashKey, string field)
        {
            return RedisBase.Core.RemoveEntryFromHash(hashKey, field);
        }

        public static bool FieldExists(string hashKey, string field)
        {
            return RedisBase.Core.HashContainsEntry(hashKey, field);
        }

        public static double IncrementFieldValue(string hashKey, string field, double amount)
        {
            return RedisBase.Core.IncrementValueInHash(hashKey, field, amount);
        }
    }
}

Set Operations

Set operations handle unique value collections with support for union, intersection, and difference operations.

using System;
using System.Collections.Generic;

namespace RedisUtility
{
    public class SetOperations : RedisBase
    {
        public static void Add(string key, string value)
        {
            RedisBase.Core.AddItemToSet(key, value);
        }

        public static void Add(string key, List<string> values)
        {
            RedisBase.Core.AddRangeToSet(key, values);
        }

        public static string GetRandomItem(string key)
        {
            return RedisBase.Core.GetRandomItemFromSet(key);
        }

        public static long GetCount(string key)
        {
            return RedisBase.Core.GetSetCount(key);
        }

        public static HashSet<string> GetAllItems(string key)
        {
            return RedisBase.Core.GetAllItemsFromSet(key);
        }

        public static string PopRandomItem(string key)
        {
            return RedisBase.Core.PopItemFromSet(key);
        }

        public static void RemoveItem(string key, string value)
        {
            RedisBase.Core.RemoveItemFromSet(key, value);
        }

        public static void MoveItem(string sourceKey, string destinationKey, string value)
        {
            RedisBase.Core.MoveBetweenSets(sourceKey, destinationKey, value);
        }

        public static HashSet<string> GetUnion(string[] keys)
        {
            return RedisBase.Core.GetUnionFromSets(keys);
        }

        public static void StoreUnion(string newKey, string[] keys)
        {
            RedisBase.Core.StoreUnionFromSets(newKey, keys);
        }

        public static void StoreDifferences(string newKey, string sourceKey, string[] keys)
        {
            RedisBase.Core.StoreDifferencesFromSet(newKey, sourceKey, keys);
        }
    }
}

Sorted Set Operations

Sorted set operations manage ordered collections with score-based ordering.

using System;
using System.Collections.Generic;

namespace RedisUtility
{
    public class SortedSetOperations : RedisBase
    {
        public static bool Add(string key, string value)
        {
            return RedisBase.Core.AddItemToSortedSet(key, value);
        }

        public static bool Add(string key, string value, double score)
        {
            return RedisBase.Core.AddItemToSortedSet(key, value, score);
        }

        public static bool AddRange(string key, List<string> values, double score)
        {
            return RedisBase.Core.AddRangeToSortedSet(key, values, score);
        }

        public static bool AddRange(string key, List<string> values, long score)
        {
            return RedisBase.Core.AddRangeToSortedSet(key, values, score);
        }

        public static List<string> GetAll(string key)
        {
            return RedisBase.Core.GetAllItemsFromSortedSet(key);
        }

        public static List<string> GetAllDescending(string key)
        {
            return RedisBase.Core.GetAllItemsFromSortedSetDesc(key);
        }

        public static IDictionary<string, double> GetAllWithScores(string key)
        {
            return RedisBase.Core.GetAllWithScoresFromSortedSet(key);
        }

        public static long GetItemIndex(string key, string value)
        {
            return RedisBase.Core.GetItemIndexInSortedSet(key, value);
        }

        public static long GetItemIndexDescending(string key, string value)
        {
            return RedisBase.Core.GetItemIndexInSortedSetDesc(key, value);
        }

        public static double GetItemScore(string key, string value)
        {
            return RedisBase.Core.GetItemScoreInSortedSet(key, value);
        }

        public static long GetCount(string key)
        {
            return RedisBase.Core.GetSortedSetCount(key);
        }

        public static long GetCountByScore(string key, double fromScore, double toScore)
        {
            return RedisBase.Core.GetSortedSetCount(key, fromScore, toScore);
        }

        public static List<string> GetRangeByHighestScore(string key, double fromScore, double toScore)
        {
            return RedisBase.Core.GetRangeFromSortedSetByHighestScore(key, fromScore, toScore);
        }

        public static List<string> GetRangeByLowestScore(string key, double fromScore, double toScore)
        {
            return RedisBase.Core.GetRangeFromSortedSetByLowestScore(key, fromScore, toScore);
        }

        public static IDictionary<string, double> GetRangeWithScoresByHighestScore(string key, double fromScore, double toScore)
        {
            return RedisBase.Core.GetRangeWithScoresFromSortedSetByHighestScore(key, fromScore, toScore);
        }

        public static List<string> GetRangeByRank(string key, int fromRank, int toRank)
        {
            return RedisBase.Core.GetRangeFromSortedSet(key, fromRank, toRank);
        }

        public static List<string> GetRangeByRankDescending(string key, int fromRank, int toRank)
        {
            return RedisBase.Core.GetRangeFromSortedSetDesc(key, fromRank, toRank);
        }

        public static bool RemoveItem(string key, string value)
        {
            return RedisBase.Core.RemoveItemFromSortedSet(key, value);
        }

        public static long RemoveRangeByRank(string key, int minRank, int maxRank)
        {
            return RedisBase.Core.RemoveRangeFromSortedSet(key, minRank, maxRank);
        }

        public static long RemoveRangeByScore(string key, double fromScore, double toScore)
        {
            return RedisBase.Core.RemoveRangeFromSortedSetByScore(key, fromScore, toScore);
        }

        public static string PopHighestScore(string key)
        {
            return RedisBase.Core.PopItemWithHighestScoreFromSortedSet(key);
        }

        public static string PopLowestScore(string key)
        {
            return RedisBase.Core.PopItemWithLowestScoreFromSortedSet(key);
        }

        public static bool ContainsItem(string key, string value)
        {
            return RedisBase.Core.SortedSetContainsItem(key, value);
        }

        public static double IncrementScore(string key, string value, double amount)
        {
            return RedisBase.Core.IncrementItemInSortedSet(key, value, amount);
        }

        public static long StoreIntersection(string newKey, string[] keys)
        {
            return RedisBase.Core.StoreIntersectFromSortedSets(newKey, keys);
        }

        public static long StoreUnion(string newKey, string[] keys)
        {
            return RedisBase.Core.StoreUnionFromSortedSets(newKey, keys);
        }
    }
}

Testing the Implementation

The following example demonstrates basic usage of the helper classes:

class Program
{
    static void Main(string[] args)
    {
        string listKey = "UserList";
        
        RedisBase.Core.FlushAll();
        
        RedisBase.Core.AddItemToList(listKey, "johndoe");
        RedisBase.Core.AddItemToList(listKey, "janesmith");
        
        RedisBase.Core.Add<string>("config_key", "config_value");
        
        StringOperations.SetValue("string_key", "abcdef");
        
        Console.ReadLine();
    }
}

This test clears all existing data, adds two items to a list, creates a string key-value pair, and demonstrates the string set operation. After execution, the data can be verified using Redis CLI clients connected to ports 6379 and 6380.

Tags: C# Redis servicestack NoSQL Caching

Posted on Fri, 31 Jul 2026 17:02:20 +0000 by dotti