C# .NET6中配置BsonSerializer序列化动态生成类的问题
MongoDB动态类型序列化问题解决方案
问题背景
开发C# .NET6应用时,通过System.Reflection动态生成类型并创建对象,调用ToBsonDocument序列化时遇到两种异常:
- 未自定义序列化器时:
Type Test is not configured as a type that is allowed to be serialized for this instance of ObjectSerializer. - 添加以下序列化器注册代码后:
System.InvalidOperationException : Sequence contains more than one element
var objectSerializer = new ObjectSerializer(type => ObjectSerializer.DefaultAllowedTypes(type) || type.Assembly.FullName.StartsWith("MyAssembly")); BsonSerializer.RegisterSerializer(objectSerializer);
遍历的对象类型均为Test,但异常仍触发。
相关代码片段
对象转Bson文档代码
foreach (var obj in source) { var document = obj.ToBsonDocument(); documents.Add(document); }
动态生成类型代码
var assemblyName = new AssemblyName("MyAssembly"); var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); var moduleBuilder = assemblyBuilder.DefineDynamicModule(typeName + "Module"); var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public); var type = typeBuilder.CreateType();
动态添加属性代码
var fieldBldrName = typeBuilder.DefineField(kvp.Key, kvp.Value, FieldAttributes.Public); var propBldrName = typeBuilder.DefineProperty(kvp.Key, PropertyAttributes.HasDefault, kvp.Value, null); var getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig; var custNameGetPropMthdBldr = typeBuilder.DefineMethod($"get_{kvp.Key}", getSetAttr, kvp.Value, Type.EmptyTypes); var custNameGetIL = custNameGetPropMthdBldr.GetILGenerator(); custNameGetIL.Emit(OpCodes.Ldarg_0); custNameGetIL.Emit(OpCodes.Ldfld, fieldBldrName); custNameGetIL.Emit(OpCodes.Ret); var custNameSetPropMthdBldr = typeBuilder.DefineMethod($"set_{kvp.Key}", getSetAttr, null, new Type[] { kvp.Value }); var custNameSetIL = custNameSetPropMthdBldr.GetILGenerator(); custNameSetIL.Emit(OpCodes.Ldarg_0); custNameSetIL.Emit(OpCodes.Ldarg_1); custNameSetIL.Emit(OpCodes.Stfld, fieldBldrName); custNameSetIL.Emit(OpCodes.Ret); propBldrName.SetGetMethod(custNameGetPropMthdBldr); propBldrName.SetSetMethod(custNameSetPropMthdBldr);
问题原因
- 第一个异常原因:MongoDB默认
ObjectSerializer仅允许序列化预定义类型集合,动态生成的Test类型不在允许范围内,触发权限校验错误。 - 第二个异常原因:直接调用
BsonSerializer.RegisterSerializer注册全局ObjectSerializer会和MongoDB内部已注册的object类型序列化器冲突,导致重复注册异常。
解决办法
方法1:为动态类型注册BsonClassMap
在动态类型创建完成后,立即注册对应的BsonClassMap,让MongoDB识别该类型的序列化规则:
var type = typeBuilder.CreateType(); // 自动映射动态类型的公共属性和字段 BsonClassMap.LookupClassMap(type);
如果需要自定义映射规则,可手动配置:
BsonClassMap.RegisterClassMap(type, cm => { cm.AutoMap(); // 示例:忽略某个属性 // cm.UnmapProperty("PropertyName"); });
方法2:扩展全局序列化器而非替换
若需保留自定义ObjectSerializer逻辑,不要直接替换全局序列化器,而是基于现有注册表扩展:
var originalRegistry = BsonSerializer.SerializerRegistry; var baseObjectSerializer = originalRegistry.GetSerializer<object>(); var customObjectSerializer = new ObjectSerializer(baseObjectSerializer, type => ObjectSerializer.DefaultAllowedTypes(type) || type.Assembly.FullName.StartsWith("MyAssembly")); var newMappings = new[] { new SerializerMapping(typeof(object), customObjectSerializer) }; var newRegistry = new SerializerRegistry(originalRegistry, newMappings); BsonSerializer.RegisterSerializerRegistry(newRegistry);
方法3:确保动态类型唯一性
若循环生成动态类型,需避免重复创建同名类型导致的冲突:
var existingType = Assembly.Load("MyAssembly").GetType(typeName); if (existingType == null) { var type = typeBuilder.CreateType(); // 注册BsonClassMap BsonClassMap.LookupClassMap(type); }
内容的提问来源于stack exchange,提问作者Jan
相关产品推荐
相关产品推荐

