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

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);

问题原因

  1. 第一个异常原因:MongoDB默认ObjectSerializer仅允许序列化预定义类型集合,动态生成的Test类型不在允许范围内,触发权限校验错误。
  2. 第二个异常原因:直接调用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 23:55:00