🔌 在 ASP.NET Core 中使用 Redis
📌 安裝 NuGet 套件
# StackExchange.Redis — 最常用的 .NET Redis 客戶端
dotnet add package StackExchange.Redis
# Microsoft 官方的分散式快取支援
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
📌 ConnectionMultiplexer 連線管理
ConnectionMultiplexer 是 StackExchange.Redis 的核心類別,負責管理與 Redis 的連線。
using StackExchange.Redis;
// ❌ 錯誤:每次都建立新連線(非常昂貴!)
public Product GetProduct(int id)
{
var redis = ConnectionMultiplexer.Connect("localhost:6379");
var db = redis.GetDatabase();
// ...
}
// ✅ 正確:ConnectionMultiplexer 應該是 Singleton
public class RedisConnection
{
private static readonly Lazy<ConnectionMultiplexer> _instance =
new(() => ConnectionMultiplexer.Connect("localhost:6379"));
public static ConnectionMultiplexer Instance => _instance.Value;
}
在 DI 容器中註冊
// Program.cs
builder.Services.AddSingleton<IConnectionMultiplexer>(
ConnectionMultiplexer.Connect(
builder.Configuration.GetConnectionString("Redis")!));
📌 IDatabase 基本操作
public class RedisCacheService
{
private readonly IDatabase _db;
public RedisCacheService(IConnectionMultiplexer redis)
{
_db = redis.GetDatabase();
}
// 存入快取
public async Task SetAsync<T>(string key, T value, TimeSpan? expiry = null)
{
var json = JsonSerializer.Serialize(value);
await _db.StringSetAsync(key, json, expiry);
}
// 從快取取得
public async Task<T?> GetAsync<T>(string key)
{
var value = await _db.StringGetAsync(key);
if (value.IsNullOrEmpty) return default;
return JsonSerializer.Deserialize<T>(value!);
}
// 刪除快取
public async Task RemoveAsync(string key)
{
await _db.KeyDeleteAsync(key);
}
// 檢查是否存在
public async Task<bool> ExistsAsync(string key)
{
return await _db.KeyExistsAsync(key);
}
}
📌 使用 IDistributedCache 介面
ASP.NET Core 提供了 IDistributedCache 抽象介面,可以輕鬆切換快取實作:
// Program.cs — 註冊 Redis 分散式快取
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = builder.Configuration
.GetConnectionString("Redis");
options.InstanceName = "DevLearn:";
});
// 使用 IDistributedCache
public class ProductService
{
private readonly IDistributedCache _cache;
private readonly AppDbContext _db;
public ProductService(IDistributedCache cache, AppDbContext db)
{
_cache = cache;
_db = db;
}
public async Task<Product?> GetProductAsync(int id)
{
var cacheKey = $"product:{id}";
// 1. 嘗試從快取取得
var cached = await _cache.GetStringAsync(cacheKey);
if (cached != null)
return JsonSerializer.Deserialize<Product>(cached);
// 2. 快取沒有,查資料庫
var product = await _db.Products.FindAsync(id);
if (product == null) return null;
// 3. 放入快取
var options = new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5),
SlidingExpiration = TimeSpan.FromMinutes(2)
};
await _cache.SetStringAsync(cacheKey,
JsonSerializer.Serialize(product), options);
return product;
}
}
📌 appsettings.json 連線設定
{
"ConnectionStrings": {
"Redis": "localhost:6379,abortConnect=false,connectTimeout=5000"
}
}
常用連線參數
| 參數 | 說明 | 預設值 |
|---|---|---|
abortConnect |
連線失敗是否拋例外 | true |
connectTimeout |
連線逾時(ms) | 5000 |
password |
密碼 | 無 |
ssl |
是否用 SSL | false |
defaultDatabase |
預設資料庫索引 | 0 |
asyncTimeout |
非同步操作逾時 | 5000 |
📌 完整範例:Product API + Redis 快取
// Controllers/ProductsController.cs
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly AppDbContext _db;
private readonly IDistributedCache _cache;
private readonly ILogger<ProductsController> _logger;
public ProductsController(
AppDbContext db,
IDistributedCache cache,
ILogger<ProductsController> logger)
{
_db = db;
_cache = cache;
_logger = logger;
}
[HttpGet("{id}")]
public async Task<IActionResult> GetProduct(int id)
{
var cacheKey = $"product:{id}";
// 嘗試快取
var cached = await _cache.GetStringAsync(cacheKey);
if (cached != null)
{
_logger.LogInformation("Cache HIT for product {Id}", id);
return Ok(JsonSerializer.Deserialize<Product>(cached));
}
_logger.LogInformation("Cache MISS for product {Id}", id);
// 查 DB
var product = await _db.Products.FindAsync(id);
if (product == null) return NotFound();
// 寫入快取(5 分鐘過期)
await _cache.SetStringAsync(cacheKey,
JsonSerializer.Serialize(product),
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)
});
return Ok(product);
}
[HttpPut("{id}")]
public async Task<IActionResult> UpdateProduct(int id, Product updated)
{
var product = await _db.Products.FindAsync(id);
if (product == null) return NotFound();
product.Name = updated.Name;
product.Price = updated.Price;
await _db.SaveChangesAsync();
// 更新後清除快取
await _cache.RemoveAsync($"product:{id}");
_logger.LogInformation("Cache INVALIDATED for product {Id}", id);
return Ok(product);
}
}
🔑 重點整理
- StackExchange.Redis 是 .NET 最常用的 Redis 客戶端
- ConnectionMultiplexer 必須是 Singleton,避免重複建立連線
- IDistributedCache 是 ASP.NET Core 的標準快取介面
- 連線字串放在 appsettings.json,透過 DI 注入使用
- 更新資料後,記得 清除對應的快取