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

为何目标类含参数化构造函数时AutoMapper无法将字符串转布尔?

问题描述

我有一个带参数化构造函数的Destination类,尝试使用AutoMapper将Source对象映射到Destination。Id的映射正常,但InStore的自定义映射似乎无法正常工作。以下是我的代码:

using AutoMapper;

var config = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<Source, Destination>()
       .ForMember(dest => dest.Id, opt => opt.MapFrom(src => int.Parse(src.Id)))
       .ForMember(dest => dest.InStore, opt => opt.MapFrom(src => src.InStore == "1"));
});

var mapper = config.CreateMapper();

var source = new Source { Id = "123", InStore = "1" };
var destination = mapper.Map<Destination>(source);

Console.WriteLine($"Id: {destination.Id}, InStore: {destination.InStore}");

public class Source
{
    public string Id { get; set; }
    public string InStore { get; set; }
}

public class Destination
{
    public int Id { get; private set; }
    public bool InStore { get; private set; }

    public Destination(int id, bool inStore)
    {
        Id = id;
        InStore = inStore;
    }
}

抛出异常:

AutoMapper.AutoMapperMappingException: 'Error mapping types.'
FormatException: String '1' was not recognized as a valid Boolean.

当我移除Destination的参数化构造函数时,映射能正常工作;当设置var source = new Source { Id = "123", InStore = "true" };时,映射也能正常工作。这些现象让我困惑:为何在存在构造函数且InStore设为"1"时映射会失败?

生成的表达式树如下:

(Source src, Destination dest, ResolutionContext ctxt) => {
    Destination typeMapDestination;
    return src == default(Source) ? default(Destination) : (
        typeMapDestination = dest ?? new Destination((
            string resolvedValue,
            resolvedValue = false || src == null ? default(string) : src.Id,
            Convert.ToInt32(resolvedValue)
        ), (
            string resolvedValue,
            resolvedValue = false || src == null ? default(string) : src.InStore,
            Convert.ToBoolean(resolvedValue)
        )),
        if (dest != null) {
            try {
                (
                    string resolvedValue,
                    int propertyValue,
                    resolvedValue = false || src == null ? default(string) : src.Id,
                    propertyValue = Convert.ToInt32(resolvedValue),
                    typeMapDestination.Id = propertyValue
                )
            } catch (Exception ex) {
                throw new AutoMapperMappingException("Error mapping types.", ex, #TypePair, #TypeMap, #PropertyMap);
            }
        },
        if (dest != null) {
            try {
                (
                    string resolvedValue,
                    bool propertyValue,
                    resolvedValue = false || src == null ? default(string) : src.InStore,
                    propertyValue = Convert.ToBoolean(resolvedValue),
                    typeMapDestination.InStore = propertyValue
                )
            } catch (Exception ex) {
                throw new AutoMapperMappingException("Error mapping types.", ex, #TypePair, #TypeMap, #PropertyMap);
            }
        },
        try {
            (
                string resolvedValue,
                resolvedValue = try {
                    false || src == null ? default(string) : src.Id;
                } catch (NullReferenceException) {
                    default(string);
                } catch (ArgumentNullException) {
                    default(string);
                },
                typeMapDestination.Name = resolvedValue
            )
        } catch (Exception ex) {
            throw new AutoMapperMappingException("Error mapping types.", ex, #TypePair, #TypeMap, #PropertyMap);
        },
        typeMapDestination
    );
}

看起来ForMember未按预期工作,我该如何修改才能实现预期结果?


问题原因与解决方案

问题原因

AutoMapper在处理带参数化构造函数的目标类时,优先通过构造函数参数完成实例初始化,而非直接映射属性。你的Destination构造函数参数inStore会被AutoMapper自动关联到Source的InStore属性,但它默认用Convert.ToBoolean(src.InStore)转换字符串——而Convert.ToBoolean仅识别"True"/"False"(大小写无关),无法解析"1"/"0",因此抛出格式异常。

你配置的ForMember规则仅在属性赋值阶段生效,但构造函数初始化时已经触发异常,根本无法进入后续步骤。

解决方案

方案1:配置构造函数参数的映射规则

直接指定构造函数中id和inStore参数的映射逻辑,覆盖AutoMapper的默认行为:

var config = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<Source, Destination>()
       .ForCtorParam("id", opt => opt.MapFrom(src => int.Parse(src.Id)))
       .ForCtorParam("inStore", opt => opt.MapFrom(src => src.InStore == "1"));
});

方案2:使用ConstructUsing自定义对象创建逻辑

手动接管Destination实例的创建过程,完全自定义构造逻辑:

var config = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<Source, Destination>()
       .ConstructUsing(src => new Destination(int.Parse(src.Id), src.InStore == "1"));
});

两种方案均可解决问题:若构造函数参数较多,ForCtorParam更灵活;若构造逻辑简单,ConstructUsing更直接直观。

内容的提问来源于stack exchange,提问作者TonWin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 23:22:36