C# MongoDB驱动Fluent API如何映射嵌套复杂类层级结构
实现方案
你定义的类层级完全可以在MongoDB C#驱动中完成映射。MongoDB为文档型数据库,原生支持嵌套文档结构,刚好匹配你需要的从属实体、根实体删除时级联清空从属数据的需求——InvoiceNumber、Invoice、InvoiceEntry均作为嵌入文档存储在Beneficiary根文档中,删除根文档时所有嵌套数据会同步删除,无需额外配置级联规则。
有两个前置注意事项:
InvoiceNumber.IssueDate、Invoice.IssueDate为无setter的只读属性,MongoDB C#驱动默认不会自动映射这类属性,需要手动指定映射规则,或全局开启只读属性映射,否则字段值不会持久化到数据库。- 所有嵌套类型不需要配置Id生成规则,也不需要映射为独立集合,仅需注册类映射规则即可被正确序列化。
基础Fluent API配置
// 注册最内层嵌套类型InvoiceEntry映射 BsonClassMap.RegisterClassMap<InvoiceEntry>(type => { type.AutoMap(); }); // 注册Invoice类型映射 BsonClassMap.RegisterClassMap<Invoice>(type => { type.AutoMap(); // 手动映射只读属性IssueDate type.MapProperty(prop => prop.IssueDate); type.MapProperty(prop => prop.InvoiceEntries); }); // 注册InvoiceNumber值对象映射 BsonClassMap.RegisterClassMap<InvoiceNumber>(type => { type.AutoMap(); // 手动映射只读属性IssueDate type.MapProperty(prop => prop.IssueDate); type.MapProperty(prop => prop.Current).SetIsRequired(true); }); // 补全Beneficiary根实体映射 BsonClassMap.RegisterClassMap<Beneficiary>(type => { type.MapIdProperty(prop => prop.Id) .SetIdGenerator(new StringObjectIdGenerator()) .SetSerializer(new StringSerializer(BsonType.ObjectId)); type.MapProperty(prop => prop.Name) .SetIsRequired(true); // 映射复杂类型属性 type.MapProperty(prop => prop.InvoiceNumber).SetIsRequired(true); type.MapProperty(prop => prop.Invoices); });
可选全局配置
如果项目中存在大量只读属性、或需要统一序列化规则,可以注册全局约定包减少重复配置:
var conventionPack = new ConventionPack { // 遇到类中未定义的字段时不抛出反序列化异常 new IgnoreExtraElementsConvention(true), // 全局自动映射只读属性 new MapReadOnlyPropertiesConvention(true), // 自动映射所有公共属性,无需逐个调用AutoMap new AutoMapConvention() }; ConventionRegistry.Register("DefaultConventions", conventionPack, _ => true);
开启全局配置后,仅需编写Beneficiary根实体的特殊映射规则(比如Id生成逻辑、必填字段规则),嵌套类型的常规属性会被自动映射。
存储结构验证
配置完成后,单条Beneficiary数据在MongoDB中会存储为如下结构,与你预期的从属层级完全一致:
{ "_id": ObjectId("自动生成的对象ID"), "Name": "受益人名称", "InvoiceNumber": { "Current": "当前发票号", "IssueDate": ISODate("发票号签发日期") }, "Invoices": [ { "Number": "发票编号", "IssueDate": ISODate("发票签发日期"), "InvoiceEntries": [ { "BillableHours": 12.5 } ] } ] }
查询嵌套文档字段时直接使用点号路径编写过滤条件即可,例如查询包含指定编号发票的受益人:
var filter = Builders<Beneficiary>.Filter.Eq("Invoices.Number", "INV-2024-001");
内容的提问来源于stack exchange,提问作者user19510842
相关产品推荐
相关产品推荐

