当目标类有参数化构造函数时,为什么 AutoMapper 无法将字符串转换为 bool?

问题描述 投票:0回答:1

我有一个类

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
没有按预期工作。我该如何修改才能达到想要的效果?

c# automapper
1个回答
0
投票

这是因为

MapFrom
使用
Expression<Func<TSource, TSourceMember>>
而不是
Func<TSource, TSourceMember>
AutoMapper
没有评估您的表达式,因此,
AutoMapper
尝试从
InStore
解析实际值,并且没有将
1
的值识别为有效的布尔值。

您可以通过执行以下操作之一来解决此问题:

  1. 使用类似
    opt.MapFrom(src => IsInStore(src.InStore))
  2. 的方法(或扩展方法)
  3. 使用不同的
    Property
    存储转换后的值并在映射器配置文件中重用它。
  4. 使用
    ValueConverter
    (这是要走的路)。

最好实现自定义

ValueConverter
,然后在映射器配置文件中使用它(它可以与任何配置文件重用)。

类似的东西:

public class StringToBooleanConverter : IValueConverter<string, bool>
{
    // you can add other truthy values that is used in your system
    // such as Active, Yes, Valid ..etc.
    private static readonly string[] _true = {"1", "TRUE"};
    
    public bool Convert(string sourceMember, ResolutionContext context)
    {
        return !string.IsNullOrWhiteSpace(sourceMember) && _true.Contains(sourceMember.ToUpper());
    }
}

然后你可以这样做:

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.ConvertUsing(new StringToBooleanConverter()));
});
© www.soinside.com 2019 - 2024. All rights reserved.