public class Person
{
Name { get; set; }
IEnumerable<Address> Addresses { get; set; }
}
public class PersonModel
{
Name { get; set; }
IEnumerable<AddressModel> Addresses { get; set; }
}
如果我像这样将
Person
映射到PersonModel
:
Mapper.DynamicMap<Person, PersonModel>(person);
如果
Addresses
上的 Person
属性为空,则它们在 PersonModel
上映射为空 Enumerable
而不是空值。
如何让
PersonModel
具有空的 Addresses
而不是空的 Enumerable
?
简单的答案是使用
AllowNullCollections
:
AutoMapper.Mapper.Initialize(cfg =>
{
cfg.AllowNullCollections = true;
});
或者如果您使用实例 API
new MapperConfiguration(cfg =>
{
cfg.AllowNullCollections = true;
}
除了在
映射器配置初始化中设置
AllowNullCollections
(如this answer中所述),您还可以选择在AllowNullCollections
定义中设置Profile
,如下所示:
public class MyMapper : Profile
{
public MyMapper()
{
// Null collections will be mapped to null collections instead of empty collections.
AllowNullCollections = true;
CreateMap<MySource, MyDestination>();
}
}
所以可能有几种方法可以使用 Automapper 完成此操作,这只是其中一种:
Mapper.CreateMap<Person, PersonMap>()
.AfterMap( (src, dest) => dest.Addresses = dest.Addresses?.Any() ? dest.Addresses : null );
此代码使用新的 c#
?.
运算符来实现空安全,因此如果您不能在代码中使用该功能,则可能需要删除它并检查是否为空。
另一种替代方法是使用条件,因此仅在值不为空时映射值。 这可能需要该值默认为空(因为它不会被映射)
Mapper.CreateMap<Person, PersonModel>()
.ForMember(
dest => dest.Addresses,
opt => opt => opt.Condition(source=> source.Addresses!= null));
您应该能够为您想要此行为的属性定义自定义解析器。所以像:
Mapper.CreateMap<Address, AddressModel>();
Mapper.CreateMap<Person, PersonModel>()
.ForMember(
dest => dest.Addresses,
opt => opt.ResolveUsing(person => person.Addresses.Any() ? person.Addresses.Select(Mapper.Map<Address, AddressModel>) : null));
如果您希望将此作为 API 中的一般规则,您可以像这样在配置服务方法中配置 Automapper。
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddControllers();
[...]
// configure automapping
services.AddAutoMapper(cfg => cfg.AllowNullCollections = true, typeof(Startup));
}
或者,在单独的 DLL 中使用自动映射的情况下(例如:对于 DTO 服务),我更喜欢使用辅助函数,所以配置也应该在那里完成:
public static class MappingHelper
{
private static readonly Lazy<IMapper> _lazy = new(() =>
{
var config = new MapperConfiguration(cfg =>
{
// This line ensures that internal properties are also mapped over.
cfg.ShouldMapProperty = p => p.GetMethod.IsPublic || p.GetMethod.IsAssembly;
cfg.AddProfile<DomainToRepositoryProfile>();
cfg.AddProfile<RepositoryToDomainProfile>();
cfg.AllowNullCollections = true;
});
var mapper = config.CreateMapper();
return mapper;
});
public static IMapper Mapper => _lazy.Value;
}
我无法使用 AutoMapper 12.0.1 和 Entity Framework Core 6.0.15 脚手架实体让 AllowNullCollections 与 net6 一起工作。唯一有效的方法是:
internal class UserDtoMapper : Profile
{
public UserDtoMapper()
{
CreateMap<User, UserDto>()
.ForMember(dest => dest.Votes,
x => x.Condition(src => src.Votes != null && src.Votes.Any()))
.ReverseMap();
}
}
public partial class User
{
// ...
public virtual ICollection<Vote> Votes { get; set; }
}
public class UserDto
{
// ...
public List<VoteDto>? Votes { get; set; }
}