| | |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 安全更新:仅当内存缓存中的值与expectedVersion匹配时才更新 |
| | | /// 防止并发写入时旧值覆盖新值 |
| | | /// </summary> |
| | | /// <typeparam name="T">值类型</typeparam> |
| | | /// <param name="key">缓存键</param> |
| | | /// <param name="newValue">新值</param> |
| | | /// <param name="expectedVersion">期望的版本(通常是旧对象的哈希值或时间戳)</param> |
| | | /// <param name="versionExtractor">从对象提取版本号的函数</param> |
| | | /// <param name="expireSeconds">过期时间</param> |
| | | /// <returns>是否更新成功</returns> |
| | | public bool TrySafeUpdate<T>( |
| | | string key, |
| | | T newValue, |
| | | object? expectedVersion, |
| | | Func<T, object?> versionExtractor, |
| | | int expireSeconds = -1) where T : class |
| | | { |
| | | var fullKey = BuildKey(key); |
| | | |
| | | // 从Redis获取当前值 |
| | | string? existingJson = null; |
| | | T? existingValue = default; |
| | | if (RedisAvailable) |
| | | { |
| | | try |
| | | { |
| | | var value = _connectionManager.GetDatabase().StringGet(fullKey); |
| | | if (!value.IsNullOrEmpty) |
| | | { |
| | | existingJson = value.ToString(); |
| | | existingValue = _serializer.Deserialize<T>(existingJson); |
| | | } |
| | | } |
| | | catch { } |
| | | } |
| | | |
| | | // 如果Redis不可用,从内存缓存获取 |
| | | if (existingValue == null && _options.EnableL1Cache) |
| | | { |
| | | if (_memoryCache.TryGetValue(fullKey, out string? cached) && cached != null) |
| | | { |
| | | existingValue = _serializer.Deserialize<T>(cached); |
| | | existingJson = cached; |
| | | } |
| | | else |
| | | { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | // 检查版本是否匹配 |
| | | if (existingValue != null) |
| | | { |
| | | var currentVersion = versionExtractor(existingValue); |
| | | if (!Equals(currentVersion, expectedVersion)) |
| | | { |
| | | _logger.LogWarning("TrySafeUpdate版本不匹配, key={Key}, expected={Expected}, current={Current}", |
| | | key, expectedVersion, currentVersion); |
| | | return false; // 版本不匹配,拒绝更新 |
| | | } |
| | | } |
| | | |
| | | // 版本匹配,执行更新 |
| | | var newJson = _serializer.Serialize(newValue); |
| | | |
| | | // 先写入Redis |
| | | if (RedisAvailable) |
| | | { |
| | | try |
| | | { |
| | | var expiry = expireSeconds > 0 ? TimeSpan.FromSeconds(expireSeconds) : (TimeSpan?)null; |
| | | if (!_connectionManager.GetDatabase().StringSet(fullKey, newJson, expiry)) |
| | | { |
| | | _logger.LogWarning("Redis TrySafeUpdate写入失败, key={Key}", key); |
| | | return _options.FallbackToMemory && _options.EnableL1Cache; |
| | | } |
| | | else |
| | | { |
| | | _logger.LogInformation("Redis TrySafeUpdate写入成功, key={Key}", key); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogWarning(ex, "Redis TrySafeUpdate写入失败, key={Key}", key); |
| | | return _options.FallbackToMemory && _options.EnableL1Cache; |
| | | } |
| | | } |
| | | |
| | | // 更新内存缓存 |
| | | if (_options.EnableL1Cache) |
| | | { |
| | | SetMemoryCache(fullKey, newJson, expireSeconds, false); |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | public object? Get(Type type, string key) |
| | | { |
| | | var fullKey = BuildKey(key); |