# CSRedisCore: the .NET Redis client with redis-cli method names and a concurrent performance caveat

> CSRedisCore is a MIT-licensed .NET client for Redis, Redis Sentinel and Redis Cluster that keeps every method name consistent with redis-cli. The README benchmark shows CSRedisCore faster for sequential operations but three times slower under Task.WaitAll concurrency, where StackExchange.Redis pulls ahead.

**2881099/csredis** — .NET Core or .NET Framework 4.0+ client for Redis and Redis Sentinel (2.8) and Cluster. Includes both synchronous and asynchronous clients. 

- Repository: https://github.com/2881099/csredis
- Stars: 2,054 · Forks: 422
- Language: C#
- License: MIT
- Published: 2026-10-09 · Updated: 2026-10-09 · Language: en
- Canonical page: https://hysenlabs.com/projects/2881099-csredis

## Installing CSRedisCore from NuGet and what the twelve connection string parameters control

The NuGet package is named CSRedisCore, and the install command is:

```bash
dotnet add package CSRedisCore
```

A connection to a single Redis instance runs through one string:

```csharp
var csredis = new CSRedis.CSRedisClient("127.0.0.1:6379,password=123,defaultDatabase=13,prefix=my_");
```

Twelve parameters shape the connection, with defaults in a table. poolsize defaults to 50, idleTimeout to 20000 ms, connectTimeout to 5000 ms, and syncTimeout to 10000 ms. preheat accepts a number and opens that many connections at startup; the default is 5. ssl defaults to false, meaning transmission is unencrypted unless you add ssl=true to the string. autoDispose defaults to true, which registers cleanup on process exit. The user parameter supports Redis 6.0 username authentication alongside password.

asyncPipeline defaults to false. When set to true, every async method automatically batches commands through the pipeline without a separate StartPipe call. tryit defaults to 0, so by default a failed command does not retry. name sets a client-list label visible via CLIENT LIST. prefix prepends a string to every key, and the actual behavior is csredis.Set(prefix + "key", 111). IPv6 addresses are supported through bracket notation: [fe80::b164:55b3:4b4f:7ce6%15]:6379.

## Method names match redis-cli, and version requirements differ by feature group

CSRedisCore keeps all method names on CSRedisClient and RedisHelper consistent with redis-cli. If you know SET, GET, HSET, SUBSCRIBE and PUBLISH from the command line, those same names appear in the .NET API without any mapping layer. This is a documented design choice, not a coincidence, and it applies to both the direct class and the static wrapper.

Feature coverage depends on the Redis server version in your deployment. Geo type commands need redis-server 3.2 or above. Stream type commands need redis-server 5.0 or above. Cluster mode relies on redis-trib.rb. Sentinel support is specified for version 2.8.

The library ships two client shapes. CSRedisClient is the direct class, intended as a singleton. RedisHelper is a static wrapper around a single CSRedisClient instance, and the README recommends it for single-instance setups. When a project needs multiple Redis connections, the pattern is either an array of CSRedisClient objects indexed by database number, or multiple subclasses of the generic RedisHelper<T>:

```csharp
public abstract class MyHelper1 : RedisHelper<MyHelper1> {}
public abstract class MyHelper2 : RedisHelper<MyHelper2> {}
```

Each subclass receives its own Initialization call with a separate CSRedisClient, and the two operate independently from that point.

## The benchmark shows CSRedisCore faster for sequential operations and slower under Task.WaitAll

A benchmark table in the repository compares CSRedisCore against StackExchange.Redis at 100,000 operations. For sequential string writes, CSRedisCore Set takes 6101 ms versus StackExchange.Redis StringSet at 7882 ms. For sequential string reads, CSRedisCore Get is 5762 ms against 7729 ms. The async single-operation numbers follow the same direction: CSRedisCore SetAsync at 6315 ms versus 8094 ms, and CSRedisCore GetAsync at 5960 ms versus 7986 ms.

The reversal appears in the Task.WaitAll concurrent case. CSRedisCore SetAsync with Task.WaitAll is 559 ms, while StackExchange.Redis StringSetAsync with concurrent Task.WaitAll is 172 ms. CSRedisCore GetAsync with Task.WaitAll is 435 ms, while StackExchange.Redis StringGetAsync concurrent is 176 ms. StackExchange.Redis is roughly three times faster in the high-concurrency scenario.

This gap is the number to read before choosing between the two libraries. If your application sends one command at a time, CSRedisCore's sequential numbers look favorable. If your code fires large batches of async commands simultaneously through Task.WaitAll, the results go the other way, and the numbers go the other way.

No conditions are documented for the benchmark: not the Redis server version, not the hardware, not the network setup between client and server. Read them as a directional comparison rather than a reproducible result.

## Pipeline mode and asyncPipeline work differently, and official cluster blocks both

StartPipe packages multiple commands into one network round trip:

```csharp
var ret1 = RedisHelper.StartPipe(p => p.Set("a", "1").Get("a"));
```

Setting asyncPipeline=true in the connection string changes async methods to batch automatically, without a StartPipe call. The benchmark data in the repository gives 450 ms for 100,000 concurrent operations with asyncPipeline enabled, offered as a single data point.

Redis official cluster removes pipeline support at the protocol level. CSRedisCore's cluster section explicitly lists multi-key commands, pipelines and Eval as unsupported in official cluster mode. This is not a client-side gap; it is a protocol constraint that applies to any Redis client in cluster mode.

PSubscribe accepts pattern arrays and handles two specific problems: in partition mode, wildcard patterns can match all nodes, so all nodes must subscribe; when multiple patterns in one group all match the same message, the handler runs only once per message rather than once per matching pattern.

## Sentinel setup passes all sentinel addresses and supports a read-only third argument

Connecting to a Redis Sentinel setup passes the master name as the first argument and the sentinel node addresses as a second array:

```csharp
var csredis = new CSRedis.CSRedisClient("mymaster,password=123,prefix=my_", 
  new [] { "192.169.1.10:26379", "192.169.1.11:26379", "192.169.1.12:26379" });
```

The first argument carries the master name and the same connection string parameters available for single-node setups: password, prefix, poolsize, ssl and the rest remain available there. The master name is what the sentinel cluster tracks for failover; changing the active Redis master does not require a client-side update as long as the sentinel addresses remain stable.

A read-only form passes false as a third argument:

new CSRedisClient("mymaster,password=123", new [] { Sentinels }, false)

No documentation explains what the third argument does in terms of routing or which node a read-only client targets. That line appears without further explanation.

All three sentinel addresses in the example are on port 26379, which is the Redis Sentinel default. The README does not document what happens if one sentinel address is unreachable at startup.

## Cluster auto-discovers nodes via MOVED and ASK errors, with a testcluster setting that breaks cloud providers

Cluster mode starts from a single connection string and builds its node list from MOVED and ASK errors the Redis server returns. As the server returns those errors, the client records slot assignments and adds nodes to its internal Nodes property automatically. One CSRedisClient pointed at one node is enough to get started; the rest populate at runtime.

For Alibaba Cloud and Tencent Cloud cluster setups, testcluster must be set to false. Those environments route slots internally and do not return MOVED errors in the standard way, so the auto-discovery path does not function there. The default is testcluster=true.

Setting prefix in the connection string creates a keySlot mismatch. When prefix is present, the keySlot calculation disagrees with the server's calculation, and the slotCache never builds correctly. The fix is either to omit prefix entirely or to set the same prefix on every node.

The official cluster also removes several operations that work in single-node and Sentinel modes. Multi-key commands, pipelines and Eval are unavailable in cluster mode. These are protocol constraints in Redis Cluster, not gaps specific to this library.

## CacheShell reduces the get-check-deserialize-set pattern to a single call

A standard read-through cache pattern in C# takes about ten lines: fetch a cached value, check if it is empty, try to deserialize, handle deserialization errors by clearing the key, fetch from the database on a miss, serialize and store with a TTL. CacheShell wraps this into one call:

```csharp
var t1 = RedisHelper.CacheShell("test1", 10, () => Test.Select.WhereId(1).ToOne());
```

The second argument is TTL in seconds. A second overload targets a specific hash field:

```csharp
var t2 = RedisHelper.CacheShell("test", "1", 10, () => Test.Select.WhereId(1).ToOne());
```

A third form accepts an array of field keys and a factory that returns multiple tuples, used for loading several records in one cache pass:

```csharp
var t3 = RedisHelper.CacheShell("test", new [] { "1", "2" }, 10, notCacheFields => new [] {
  ("1", Test.Select.WhereId(1).ToOne()),
  ("2", Test.Select.WhereId(2).ToOne())
});
```

The README does not document the error behavior inside the factory lambda, what happens on a partial cache miss when only some fields are absent, or what behavior results from a TTL of 0. Those are questions for the issue tracker.

## v3.8.668 is from September 2022, and IDistributedCache wiring needs a second NuGet package

The most recent release tag is v3.8.668, published on 2022-09-06. The last push to the master branch was on 2026-07-06, but no release has been tagged in that interval. The repository is not archived. The gap between the last tag and the last push is not explained in the repository.

IDistributedCache integration requires a second NuGet package:

```bash
dotnet add package Caching.CSRedis
```

The wiring calls one initialization method and then registers a singleton in the service container:

```csharp
RedisHelper.Initialization(csredis);
services.AddSingleton<IDistributedCache>(new Microsoft.Extensions.Caching.Redis.CSRedisCache(RedisHelper.Instance));
```

CSRedisClient is a singleton and stays alive for the application lifetime. The Caching.CSRedis integration expects RedisHelper.Instance, so a setup that uses multiple RedisHelper<T> subclasses for multiple Redis connections must decide which one backs the IDistributedCache registration. The repository documents no guidance on that case.

The MIT license is confirmed in the repository metadata. At the bottom of the README, the original open source project at https://github.com/ctstone/csredis is credited as the starting point.

## Conclusion

CSRedisCore is a practical choice for .NET applications that need a single Redis instance or a Sentinel failover setup, and the redis-cli method naming cuts the learning gap for teams already familiar with the command line. Skip it if your deployment is a Redis Cluster that depends on multi-key commands, pipelines or Eval, since those are unavailable in official cluster mode. The benchmark reversal under Task.WaitAll is the specific number to check: if your code fires large batches of async commands simultaneously, StackExchange.Redis is roughly three times faster in that scenario. Before adopting, verify whether your cloud provider requires testcluster=false, and check the v3.8.668 release date against the Redis server version you are running.

## FAQ

### What is CSRedisCore?

CSRedisCore is a .NET client library for Redis that supports single-node, Redis Sentinel and Redis Cluster setups. It keeps method names consistent with redis-cli commands and is published on NuGet under the name CSRedisCore.

### How do I install CSRedisCore?

Run dotnet add package CSRedisCore from the command line to add the package to a .NET project. For IDistributedCache support in ASP.NET Core, a separate package Caching.CSRedis is also required.

### Does CSRedisCore support Redis Cluster?

It supports Redis Cluster through auto-discovery via MOVED and ASK errors, but official cluster mode does not support multi-key commands, pipelines or Eval. For Alibaba Cloud and Tencent Cloud clusters, the README says to set testcluster=false in the connection string.

### How does asyncPipeline work in CSRedisCore?

Setting asyncPipeline=true in the connection string makes async methods automatically batch their commands through the pipeline without a separate StartPipe call. The default is false, and pipeline mode is then accessed explicitly through RedisHelper.StartPipe.

### What is the difference between CSRedisClient and RedisHelper in CSRedisCore?

CSRedisClient is the direct singleton class for one Redis connection. RedisHelper is a static wrapper around a CSRedisClient singleton, recommended in the README for single-instance setups. Multiple connections need multiple CSRedisClient objects or multiple RedisHelper<T> subclasses.

## Sources

- [2881099/csredis on GitHub](https://github.com/2881099/csredis)
- [Issues](https://github.com/2881099/csredis/issues)
- [License: MIT](https://github.com/2881099/csredis/blob/master/LICENSE)
- [README](https://github.com/2881099/csredis/blob/master/README.md)
- [Releases](https://github.com/2881099/csredis/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/2881099-csredis
