Home / Code Samples / .NET
Verdict: StackExchange.Redis for Redis (the de facto standard, used by Stack Overflow itself). EnyimMemcached for Memcached. ASP.NET Core's IDistributedCache abstracts the choice for typical cache work.

.NET Clients for Redis vs Memcached

The .NET ecosystem has one dominant Redis client (StackExchange.Redis) and one well-known Memcached client (EnyimMemcached). ASP.NET Core's caching abstractions hide the engine choice for most application code. Microsoft Research's Garnet is an interesting parallel-universe option.

StackExchange.Redis
.NET de facto standard
Maintained by Marc Gravell
EnyimMemcached
.NET Memcached client
Long-standing, .NET Core compatible
IDistributedCache
ASP.NET Core abstraction
Swap backends in startup
Garnet
Microsoft Research RESP server
Open-sourced March 2024, MIT

StackExchange.Redis: the de facto standard

StackExchange.Redis was written by Marc Gravell and team at Stack Overflow, originally to power Stack Overflow's own Redis infrastructure. The architecture reflects that origin: one ConnectionMultiplexer instance is thread-safe and meant to be shared across the entire application, internally maintaining one logical connection per Redis node with multiplexed command pipelining for maximum throughput.

The API surface is comprehensive. IDatabase exposes every Redis command as a typed method (StringSet, HashSet, SortedSetAdd, etc.) with both sync and async variants. Pub/sub goes through ISubscriber on a separate connection. Cluster support is automatic via the Multiplexer's topology discovery. Lua script execution, transactions, batches, and condition-based execution are all first-class.

For ASP.NET Core specifically, the pattern is to register the IConnectionMultiplexer as a singleton in DI: builder.Services.AddSingleton<IConnectionMultiplexer>(sp => ConnectionMultiplexer.Connect("localhost")). Then inject it anywhere you need Redis access. The Microsoft.Extensions.Caching.StackExchangeRedis package wraps this into an IDistributedCache implementation if you want the abstracted interface.

Side-by-side: an ASP.NET Core cache call

StackExchange.Redis
// Startup
builder.Services.AddSingleton<IConnectionMultiplexer>(
  ConnectionMultiplexer.Connect("localhost"));

// In a controller / service
public class ProductService {
  private readonly IDatabase _redis;
  public ProductService(IConnectionMultiplexer mux) {
    _redis = mux.GetDatabase();
  }

  public async Task<Product?> GetAsync(int id) {
    var cached = await _redis.StringGetAsync($"prod:{id}");
    if (cached.HasValue)
      return JsonSerializer.Deserialize<Product>(cached!);

    var p = await _db.Products.FindAsync(id);
    if (p != null)
      await _redis.StringSetAsync(
        $"prod:{id}", JsonSerializer.Serialize(p),
        TimeSpan.FromMinutes(15));
    return p;
  }
}
EnyimMemcached
// Startup
builder.Services.AddEnyimMemcached(opts => {
  opts.AddServer("localhost", 11211);
});

// In a controller / service
public class ProductService {
  private readonly IMemcachedClient _mc;
  public ProductService(IMemcachedClient mc) {
    _mc = mc;
  }

  public async Task<Product?> GetAsync(int id) {
    var cached = await _mc.GetValueAsync<Product>($"prod:{id}");
    if (cached != null) return cached;

    var p = await _db.Products.FindAsync(id);
    if (p != null)
      await _mc.SetAsync(
        $"prod:{id}", p, TimeSpan.FromMinutes(15));
    return p;
  }
}

IDistributedCache: the abstraction layer

ASP.NET Core's IDistributedCache interface (in Microsoft.Extensions.Caching.Abstractions) provides a backend-agnostic cache API with GetString / SetString / Remove methods. Backend implementations include Microsoft.Extensions.Caching.StackExchangeRedis (Redis via StackExchange.Redis), Microsoft.Extensions.Caching.SqlServer (SQL Server), and several community-maintained Memcached implementations.

The pattern is to register the implementation in DI and inject the interface everywhere: builder.Services.AddStackExchangeRedisCache(opts => { opts.Configuration = "localhost"; }). Application code uses IDistributedCache; you can swap the implementation by changing the registration without touching the consumers.

The abstraction is intentionally minimal: only byte arrays as values, only string keys, only TTL-based expiry, no atomic operations beyond Get/Set/Remove. For application code that only does simple cache-aside, this is enough. For Redis-specific features (sorted sets, pub/sub, transactions, Lua scripts) you bypass IDistributedCache and use IConnectionMultiplexer directly. Most production .NET apps use both: IDistributedCache for the simple cache layer, raw StackExchange.Redis for the advanced patterns.

Microsoft Garnet: the .NET-native RESP server

Microsoft Garnet is a cache server (not a client) written in C# by Microsoft Research, open-sourced in March 2024 with MIT licensing. Garnet implements the Redis RESP protocol on the server side, so any Redis client (including StackExchange.Redis) connects and issues commands as if it were talking to Redis itself. The server is multi-threaded with NUMA-aware scheduling and supports storage tiering (in-memory plus optional NVMe spillover).

The Microsoft Research benchmarks claim significantly higher throughput than Redis on multi-core hardware due to the threading model. In practice the gains are workload-dependent; for highly parallel cache workloads on big machines Garnet shines, for single-key contention the difference narrows. Garnet supports a large subset of Redis commands (GET, SET, HSET, ZADD, LPUSH, transactions, pub/sub) but not the full surface; check the compatibility list for your specific commands before assuming drop-in replacement.

Garnet is not a managed Azure service. You run it yourself on Azure VMs, AKS, or anywhere else you would run Redis. The interesting use case is .NET-heavy teams who want a permissively-licensed Redis-compatible server with strong multi-core throughput characteristics, and who are comfortable operating a self-hosted store. For most teams the operational simplicity of using a managed Redis or Valkey on a hyperscaler outweighs the throughput-per-dollar advantage Garnet might offer on self-managed infrastructure.

FAQ

Is StackExchange.Redis still maintained?

Yes, actively. Maintained by Marc Gravell and team, originally developed inside Stack Overflow where it still powers their Redis infrastructure. Latest versions support .NET 8 / .NET 9, RESP3 protocol, and the latest Redis 8.0 commands.

EnyimMemcached or another Memcached client?

EnyimMemcached is the long-standing .NET Memcached client. Maintained but slower-moving. For new code consider also BeIT memcached or wrapping libmemcached directly, but EnyimMemcached remains the most commonly used choice.

IDistributedCache vs IMemoryCache?

IMemoryCache is in-process cache (single ASP.NET Core instance). IDistributedCache is the distributed cache abstraction with backends for Redis (Microsoft.Extensions.Caching.StackExchangeRedis), SQL Server, and others. For multi-instance deployments you need IDistributedCache; for single-instance or per-instance cache use IMemoryCache.

Microsoft Garnet, what is it?

An MIT-licensed RESP-compatible cache server from Microsoft Research, open-sourced March 2024. Written in C#, multi-threaded throughput, supports storage tiering. Not a managed Azure service. Interesting as an alternative engine to run with StackExchange.Redis as the client; the protocol compatibility means client code does not change.

How do I configure StackExchange.Redis for ASP.NET Core?

Register IConnectionMultiplexer as a singleton in DI, then inject it into services. The connection multiplexer is thread-safe and intended to be shared across the application; do not create per-request instances. For IDistributedCache, the Microsoft.Extensions.Caching.StackExchangeRedis package handles wiring automatically.

Related decisions

Java clients
Similar architecture (Lettuce vs Jedis)
Azure Cache
Where .NET shops typically host
API response caching
IDistributedCache patterns
All code samples
Hub