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

AutoMapper中PascalCase与小写下划线双向映射含数字属性失败问题

AutoMapper中PascalCase与小写下划线双向映射含数字属性失败问题

看起来你遇到了AutoMapper命名约定处理嵌入数字属性时的双向映射坑,还踩了ReverseMap()搭配全局命名约定的小陷阱,我来帮你拆解问题并给出解决方案。

问题根源

  1. 全局命名约定的反向映射冲突
    你第一个配置里全局设置了SourceMemberNamingConvention = PascalCase、Destination = LowerUnderscore,当用ReverseMap()时,反向映射的Source是DTO(下划线命名),但AutoMapper还是用全局的Pascal约定去解析Source成员,自然找不到对应关系,这就是反向映射值为0的核心原因。

  2. 默认命名约定对数字的处理缺陷
    即使你单独配置反向映射(第二个例子),AutoMapper默认的命名约定拆分逻辑对嵌入在单词中间的数字处理不够智能。比如type_1_crest转Pascal时应该是Type1Crest,但反过来Type1Crest拆分成下划线时,默认逻辑没在数字前后正确拆分,导致双向映射的名称匹配失败。

解决方案

方案一:为正向/反向映射单独配置命名约定

不要依赖全局命名约定,而是为每个映射单独指定Source和Destination的命名规则,这样ReverseMap()或者单独的反向映射都能正确识别命名风格:

var config = new MapperConfiguration(cfg =>
{
    // 正向映射:DB实体(Pascal)→ DTO(下划线)
    cfg.CreateMap<MyDbEntity, MyDto>()
        .SourceMemberNamingConvention(PascalCaseNamingConvention.Instance)
        .DestinationMemberNamingConvention(LowerUnderscoreNamingConvention.Instance)
        // 反向映射时手动反转命名约定
        .ReverseMap()
        .SourceMemberNamingConvention(LowerUnderscoreNamingConvention.Instance)
        .DestinationMemberNamingConvention(PascalCaseNamingConvention.Instance);
});

不过这个方案可能还解决不了数字的匹配问题,需要搭配方案二使用。

方案二:自定义命名约定处理数字拆分

AutoMapper默认的拆分正则没考虑数字作为分隔点的情况,我们可以自定义INamingConvention实现,让数字前后的字符被正确拆分:

// 自定义支持数字拆分的PascalCase命名约定
public class PascalCaseWithNumbersConvention : INamingConvention
{
    // 正则规则:在大写前/小写大写间/字母数字间/数字字母间拆分
    public Regex SplittingExpression => new Regex(@"(?<=[A-Z])(?=[A-Z][a-z])|(?<=[^A-Z])(?=[A-Z])|(?<=[A-Za-z])(?=[0-9])|(?<=[0-9])(?=[A-Za-z])");
    public string Separator => "";
}

// 自定义下划线命名约定(保持小写即可)
public class LowerUnderscoreWithNumbersConvention : INamingConvention
{
    public Regex SplittingExpression => new Regex(@"_");
    public string Separator => "_";
    public string Transform(string value) => value.ToLowerInvariant();
}

然后在映射中使用自定义约定:

var config = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<MyDbEntity, MyDto>()
        .SourceMemberNamingConvention(new PascalCaseWithNumbersConvention())
        .DestinationMemberNamingConvention(new LowerUnderscoreWithNumbersConvention())
        .ReverseMap()
        .SourceMemberNamingConvention(new LowerUnderscoreWithNumbersConvention())
        .DestinationMemberNamingConvention(new PascalCaseWithNumbersConvention());
});

config.AssertConfigurationIsValid(); // 现在不会报错了
var mapper = config.CreateMapper();

// 测试双向映射
var dto = mapper.Map<MyDto>(new MyDbEntity { EquipCrestSlotType1CrestId1 = 10 });
Console.WriteLine(dto.equip_crest_slot_type_1_crest_id_1); // 输出10

var dbEntity = mapper.Map<MyDbEntity>(dto);
Console.WriteLine(dbEntity.EquipCrestSlotType1CrestId1); // 输出10

额外提示

如果你不想自定义命名约定,也可以用ForMember手动指定映射,但你提到不想手动处理,所以上面的方案更适合批量处理所有带数字的属性。

备注:内容来源于stack exchange,提问作者lordnoob

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.23 07:00:30