我正在尝试在我们的项目中测试 AutoMapper 配置文件的使用情况。我有一个对象
Source
,我将其映射到 Destination
,实际上它有大约 30 个属性,但对于这个例子来说,只有两个就可以了。
现在我得到了另一个对象 SourceWithChild
,我也想将其映射到相同的目标对象。它实际上在子属性中包含所有 Source
数据,但也有一些附加数据,在本例中只有一个字段。为了映射这些,我使用ConstructUsing
,这是来自另一个堆栈溢出帖子的解决方案。这在应用程序中效果很好,但在测试中该配置对 AutoMapper 无效。
这是一个重现这一点的示例。第二个测试
AutoMapper_ConstructUsing_Test_IsValid
将失败,而另外两个测试将成功完成。
internal class Source
{
public int IntValue { get; set; }
public string? StringValue { get; set; }
}
internal class SourceWithChild
{
public Source Source { get; set; }
public string AdditionalField { get; set; }
}
internal class Destination
{
public int IntValue { get; set; }
public string? StringValue { get; set; }
public string AdditionalField { get; set; }
}
internal class ChildMappingProfile : Profile
{
public ChildMappingProfile()
{
CreateMap<Source, Destination>()
.ForMember(dest => dest.IntValue, opt => opt.MapFrom(src => src.IntValue)) // not needed, same name
.ForMember(dest => dest.StringValue, opt => opt.MapFrom(src => src.StringValue)) // not needed, same name
.ForMember(dest => dest.AdditionalField, opt => opt.Ignore())
;
}
}
internal class ConstructUsingMappingProfile : Profile
{
public ConstructUsingMappingProfile()
{
CreateMap<SourceWithChild, Destination>()
.ForMember(dest => dest.AdditionalField, opt => opt.MapFrom(src => src.AdditionalField)) // not needed, same name
.ConstructUsing((src, ctx) => ctx.Mapper.Map<Destination>(src.Source))
;
}
}
public class ConstructUsingTests
{
[Fact]
public void AutoMapper_ChildMapping_IsValid()
{
var config = new MapperConfiguration(cfg => cfg.AddProfile(new ChildMappingProfile()));
config.AssertConfigurationIsValid();
}
[Fact]
public void AutoMapper_ConstructUsing_Test_IsValid()
{
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new ChildMappingProfile());
cfg.AddProfile(new ConstructUsingMappingProfile());
});
config.AssertConfigurationIsValid();
}
[Fact]
public void AutoMapper_ConstructUsing_Test_Maps_correctly()
{
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new ChildMappingProfile());
cfg.AddProfile(new ConstructUsingMappingProfile());
});
var destination = config.CreateMapper().Map<Destination>(new SourceWithChild { AdditionalField = "2", Source = new Source { IntValue = 1, StringValue = "Test" } });
destination.Should().NotBeNull();
destination.AdditionalField.Should().Be("2");
destination.IntValue.Should().Be(1);
destination.StringValue.Should().Be("Test");
}
}
现在我可以在
ConstructUsingMappingProfile
中手动映射所有子属性,但在实际示例中,这将是大约 30 个属性,并且还会增加额外的维护成本,因为任何更改都需要在两个配置文件中进行。我想避免这种情况。
是否有另一种方法可以做到这一点,它在测试和最终应用程序中都有效?
或者这可能是一个错误?我假设测试使用不同的映射上下文,这甚至可能是设计使然。我还在 AutoMapper 存储库中发现了一个 GitHub 问题,但他们只建议在这里提问。
AutoMapper 支持使用
IncludeMembers()
方法将子对象添加到源中。
该方法的XML Doc文档:
通过将指定的子对象映射到目标对象,向当前映射添加额外的配置。需要显式创建从子类型到目标的映射。
添加
IncludeMembers()
调用,同时指定子对象的路径:
CreateMap<SourceWithChild, Destination>()
.IncludeMembers(src => src.Source);
完整的映射配置文件:
internal class ChildMappingProfile : Profile
{
public ChildMappingProfile()
{
CreateMap<Source, Destination>()
.ForMember(dest => dest.AdditionalField, opt => opt.Ignore());
}
}
internal class ConstructUsingMappingProfile : Profile
{
public ConstructUsingMappingProfile()
{
CreateMap<SourceWithChild, Destination>()
.IncludeMembers(src => src.Source);
}
}
请注意我如何删除冗余配置部分,同时仍然保持配置有效。