You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Redis批量读写时HashSet类型反序列化失败求助

问题:Redis批量读取时反序列化失败

场景说明

使用以下.NET代码实现Redis的批量写入与读取,其中Dictionary<TKey, TValue>实际为Dictionary<ClassId, HashSet<Security>>。数据已成功写入Redis,示例Key为<keyPrefix>:<SomeId>,Value为[{"Name":"Test"}],但执行批量读取方法时反序列化失败。

相关代码

public void BulkSet(Dictionary<TKey, TValue> keyValues)
{
    if (keyValues == null || keyValues.Count == 0) return;
    var batch = _db.CreateBatch();
    foreach (var item in keyValues)
    {
        var json = JsonConvert.SerializeObject(item.Value);
        batch.JsonSetAsync(ToRedisKey(item.Key), json, "$");
    }
    batch.Execute();
}

private string ToRedisKey([DisallowNull] TKey key)
{
    if (key is ICacheKey cacheKey)
        return $"{_repositoryConfig.RedisRepositoryConfig.KeyPrefix}:" + cacheKey.GenerateKey();
    else if (key is string str)
        return $"{_repositoryConfig.RedisRepositoryConfig.KeyPrefix}:{str}";
    else
        return $"{_repositoryConfig.RedisRepositoryConfig.KeyPrefix}:{key.ToString().ToLower()}";
}

private void BulkGetUsingPipelining(HashSet<TKey> keys, Dictionary<TKey, TValue> cachedItems, HashSet<TKey> nonCachedKeys)
{
    var orderedKeys = keys.ToArray();
    var batch = _db.CreateBatch();
    var tasks = orderedKeys.Select(k => batch.JsonGetAsync(ToRedisKey(k))).ToList();
    batch.Execute();
    Task.WhenAll(tasks); 

    for (var i = 0; i < tasks.Count; i++)
    {
        var result = tasks[i].Result;
        var key = orderedKeys.ElementAt(i);
        TValue value;
        if (!result.IsNull)
        {
            if (result.ToString().TrimStart().StartsWith('[')) {
                value = JsonConvert.DeserializeObject<HashSet<TValue>>(result.ToString())!.First();
                // I tried with List also
                //value = JsonConvert.DeserializeObject<List<TValue>>(result.ToString())!.First();
            }
            else
                value = JsonConvert.DeserializeObject<TValue>(result.ToString())!;
            cachedItems.Add(key, value);
        }
        else
            nonCachedKeys.Add(key);        
    }
}

报错信息

无法将当前JSON对象(例如{"name":"value"})反序列化为类型'System.Collections.Generic.HashSet`1[Digital.MarketData.Security]',因为该类型需要JSON数组(例如[1,2,3])才能正确反序列化。要解决此错误,可将JSON改为JSON数组,或更改被反序列化的类型为可从JSON对象反序列化的常规.NET类型(例如非整数等原始类型、非数组或List等集合类型)。也可向该类型添加JsonObjectAttribute以强制其从JSON对象反序列化。路径'PpaSecurityId',第1行,第17位。

问题原因与解决方案

原因分析

写入Redis时,item.Value是HashSet<Security>,序列化后得到的JSON是Security对象的数组。但读取时,错误地将JSON数组反序列化为HashSet<TValue>(即HashSet<HashSet<Security>>),而JSON数组中的元素是单个Security对象,并非HashSet,因此触发反序列化错误。

修改方案

直接将JSON数组反序列化为TValue(也就是HashSet<Security>)即可,不需要额外套一层集合再取First()。修改后的BulkGetUsingPipelining方法如下:

private void BulkGetUsingPipelining(HashSet<TKey> keys, Dictionary<TKey, TValue> cachedItems, HashSet<TKey> nonCachedKeys)
{
    var orderedKeys = keys.ToArray();
    var batch = _db.CreateBatch();
    var tasks = orderedKeys.Select(k => batch.JsonGetAsync(ToRedisKey(k))).ToList();
    batch.Execute();
    // 确保等待所有异步任务完成,避免读取未完成的结果
    Task.WhenAll(tasks).Wait(); 

    for (var i = 0; i < tasks.Count; i++)
    {
        var result = tasks[i].Result;
        var key = orderedKeys[i]; // 直接索引访问更高效
        if (!result.IsNull)
        {
            // 直接反序列化为TValue,与写入时的序列化类型保持一致
            var value = JsonConvert.DeserializeObject<TValue>(result.ToString())!;
            cachedItems.Add(key, value);
        }
        else
        {
            nonCachedKeys.Add(key);        
        }
    }
}

额外优化点

  1. 原代码中Task.WhenAll(tasks);未等待任务完成,改为Task.WhenAll(tasks).Wait();确保所有读取任务执行完毕;如果方法允许异步,更推荐改为async Task方法并使用await Task.WhenAll(tasks);。
  2. 用orderedKeys[i]替代orderedKeys.ElementAt(i),索引访问效率更高。

内容的提问来源于stack exchange,提问作者ANEES

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 04:55:56