.NET Core 3.1 MongoDB反序列化Dictionary转List类型异常排查
根因分析
异常触发点在ClauseKeysValueDocument类的ValueListDict属性实现,核心问题是序列化和反序列化逻辑不匹配:
- 属性getter直接返回
Dictionary<string, ClauseEnumDocument>类型的私有字段,MongoDB C#驱动序列化写入数据时,会直接把该值按BSON文档(字典结构)存储到MongoDB中 - 属性setter硬编码将传入值强转为
List<object>类型,反序列化读取数据时,驱动从库中读出之前写入的字典类型值,直接强转List就会抛出你看到的类型转换异常 - 原代码本意是绕开.NET Core 3.1对非string键字典序列化的限制,但只实现了数组转字典的反序列化逻辑,没有对应实现字典转数组的序列化逻辑,导致读写数据格式完全不一致
- 异常栈里提示的
ClauseDocument.KeysValue属性本身没有逻辑错误,只是因为它存储的列表元素包含ClauseKeysValueDocument实例,异常逐层向上抛出时被标记为出错属性
修复方案
调整ValueListDict属性的实现,保证序列化输出格式和反序列化预期格式一致,同时兼容历史已存储的旧数据,修正后的核心代码如下:
public class ClauseKeysValueDocument { [BsonIgnoreIfNull] public string ListSeparatorValues { get; set; } [BsonIgnoreIfNull] private Dictionary<string, ClauseEnumDocument> _ValueListDict { get; set; } [BsonIgnoreIfNull] public object ValueListDict { get { // 序列化时将字典转为setter预期的数组结构,保证读写格式统一 var arrayFormat = new List<object>(); foreach (var keyValuePair in _ValueListDict) { arrayFormat.Add(new List<object> { keyValuePair.Key, keyValuePair.Value }); } return arrayFormat; } set { // 兼容历史已经被序列化为字典格式的存量数据,无需洗数 if (value is Dictionary<string, ClauseEnumDocument> storedDict) { _ValueListDict = storedDict; return; } // 处理正常数组格式的存储值 var dictAsArray = (List<object>)value; var dict = new Dictionary<string, ClauseEnumDocument>(); foreach (var item in dictAsArray) { var keyValueItem = (List<object>)item; dict.Add(keyValueItem[0].ToString(), (ClauseEnumDocument)keyValueItem[1]); } _ValueListDict = dict; } } // 其余属性保持原有定义不变 [BsonIgnoreIfNull] public String StateCode { get; set; } [BsonIgnoreIfNull] public String RecordStateCode { get; set; } [BsonIgnoreIfNull] public DateTime StartDate { get; set; } [BsonIgnoreIfNull] public DateTime? EndDate { get; set; } [BsonIgnoreIfNull] public int ProgNumberToCancel { get; set; } [BsonIgnoreIfNull] public Int32 ProgNumber { get; set; } [BsonIgnoreIfNull] public String Value { get; set; } }
修复说明:
- 调整getter逻辑,序列化时主动将字典转为
List<object>结构,后续新写入的数据都会按数组格式存储,从根源消除类型不匹配问题 - setter增加类型判断分支,直接识别历史存量数据中已经存为字典格式的值,不需要批量修改库中已有数据即可完成兼容
- 如果后续框架升级到.NET 5及以上版本,可以删除这个自定义转换的object属性,直接将私有字典字段改为公开属性,使用MongoDB驱动原生的字典序列化能力即可,减少自定义维护成本
内容的提问来源于stack exchange,提问作者RP_PC_Net
相关产品推荐
相关产品推荐

