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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 11:54:21