AutoMapper 与 ConstructUsing 映射子属性在测试中无效,但在生产中有效

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

我正在尝试在我们的项目中测试 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 问题,但他们只建议在这里提问。

c# unit-testing automapper xunit
1个回答
1
投票

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);
    }
}

请注意我如何删除冗余配置部分,同时仍然保持配置有效。

© www.soinside.com 2019 - 2024. All rights reserved.