我有一个类
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
没有按预期工作。我该如何修改才能达到想要的效果?
这是因为
MapFrom
使用 Expression<Func<TSource, TSourceMember>>
而不是 Func<TSource, TSourceMember>
。 AutoMapper
没有评估您的表达式,因此,AutoMapper
尝试从 InStore
解析实际值,并且没有将 1
的值识别为有效的布尔值。
您可以通过执行以下操作之一来解决此问题:
opt.MapFrom(src => IsInStore(src.InStore))
Property
存储转换后的值并在映射器配置文件中重用它。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()));
});