MongoDB C# Driver使用internal构造函数无法正常持久化DDD值对象
问题根因
MongoDB C# 2.13版本驱动默认的类序列化规则,会优先扫描类型的公共构造函数作为序列化/反序列化的入口,当没有公共构造函数时,默认不会触发属性的序列化逻辑,因此会将值对象序列化为空BSON文档。以下三个方案都可以在保留值对象internal构造函数的前提下解决问题:
方案1:使用[BsonConstructor]特性标记internal构造函数
直接在值对象的internal构造函数上添加[BsonConstructor]特性,驱动会通过反射调用该internal构造函数,同时正常扫描公共属性完成序列化。
注意:该方案会让领域层直接依赖MongoDB.Bson包,如果遵循DDD分层原则不希望领域层引入基础设施依赖,不推荐使用。
示例代码:
public class Information : Value<Information> { public string Text { get; } [BsonConstructor] internal Information(string text) { Text = text; } // 其余代码保持不变 }
方案2:自定义Bson序列化器(推荐,符合DDD分层原则)
在基础设施层实现Information类的自定义序列化器,不需要修改领域层的任何代码,也不需要调整构造函数的访问权限,完全不破坏领域模型的封装性。
步骤1:实现自定义序列化器
public class InformationSerializer : SerializerBase<Information> { public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, Information value) { if (value == null) { context.Writer.WriteNull(); return; } context.Writer.WriteString(value.Text); } public override Information Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { var bsonType = context.Reader.GetCurrentBsonType(); if (bsonType == BsonType.Null) { context.Reader.ReadNull(); return null; } var text = context.Reader.ReadString(); // 直接调用领域层提供的公共工厂方法创建实例,天然符合领域校验规则 return Information.CreateInformation(text); } }
步骤2:全局注册序列化器
在应用启动阶段执行注册逻辑即可:
BsonSerializer.RegisterSerializer(new InformationSerializer());
方案3:配置程序集内部可见性
在领域层项目的程序集配置中添加可见性声明,允许MongoDB相关程序集访问领域层的internal成员,不需要修改现有业务代码。
如果项目存在AssemblyInfo.cs文件,直接添加以下配置:
[assembly: InternalsVisibleTo("MongoDB.Bson")] [assembly: InternalsVisibleTo("MongoDB.Driver")]
如果是SDK风格的csproj项目,可以直接在csproj文件中添加:
<ItemGroup> <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo"> <_Parameter1>MongoDB.Bson</_Parameter1> </AssemblyAttribute> <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo"> <_Parameter1>MongoDB.Driver</_Parameter1> </AssemblyAttribute> </ItemGroup>
注意:该方案会开放领域层所有internal成员的访问权限给MongoDB程序集,权限粒度较粗。
内容的提问来源于stack exchange,提问作者Cladoo
相关产品推荐
相关产品推荐

