protobuf.net 3.0.*创建RuntimeTypeModel时抛出InvalidOperationException问题咨询
问题分析与解决方案
这是protobuf-net 3.0+版本中模型冻结逻辑的行为变化导致的问题,你的代码在2.4.6版本正常是因为旧版本对模型锁定的时机要求更宽松,而3.x版本收紧了规则。
具体原因
在protobuf-net 3.x中,调用model.CanSerialize()会触发元数据的早期冻结——这个方法内部需要确认类型的序列化能力,会提前完成部分类型的初始化工作,直接导致相关的MetaType实例被标记为不可修改。而你在调用CanSerialize()之后,尝试给已经被冻结的metaTypeOuter添加字段,就会抛出InvalidOperationException(提示类型生成序列化器后无法修改)。
修复方案
核心思路是避免在模型配置完成前触发冻结操作,可以通过调整代码顺序或简化不必要的检查来解决:
方案1:调整操作顺序,先添加字段再做检查
把字段添加的逻辑放在CanSerialize()检查之前,确保字段配置完成后再触发可能的冻结:
RuntimeTypeModel MakeRTM(Type type) { var model = RuntimeTypeModel.Create(); MetaType metaTypeOuter = model.Add(type, true); Type baseType = type.BaseType; MetaType metaTypeInner = model.Add(baseType, true); metaTypeInner.AddSubType(1, type); int index = 0; foreach( var fld in type.GetFields() ) { // 先添加字段,再执行序列化检查 metaTypeOuter.Add(++index, fld.Name); // 此时即使触发冻结,字段已经配置完成 if(!model.CanSerialize(fld.FieldType)) model.Add(fld.FieldType, true); } return model; }
方案2:移除不必要的检查(更简洁)
由于你的场景中AnotherClass已经被显式添加到模型中,model.CanSerialize(fld.FieldType)必然返回true,这段检查完全可以省略,直接配置字段即可:
RuntimeTypeModel MakeRTM(Type type) { var model = RuntimeTypeModel.Create(); MetaType metaTypeOuter = model.Add(type, true); Type baseType = type.BaseType; MetaType metaTypeInner = model.Add(baseType, true); metaTypeInner.AddSubType(1, type); int index = 0; foreach( var fld in type.GetFields() ) { metaTypeOuter.Add(++index, fld.Name); } return model; }
通用建议
如果你的场景涉及更多不确定的类型,最稳妥的方式是先将所有需要的类型都添加到模型中,再配置字段、子类型等关系,彻底避免提前冻结的问题。
内容的提问来源于stack exchange,提问作者Ian Spratt
相关产品推荐
相关产品推荐

