使用相同的对象和不同的TypeConverters实现AutoMapper的双向映射

3
我想使用AutoMapper将DTO映射到模型。但我的项目包含一些自定义规则,用于原始类型的默认值,例如[string null value is "_"], [default int value is -1] and [date time fields must be integer like 19990221]等。
在我的测试中,我有一个外部DTO,其中包含Null字符串值。但我的内部系统模型有一个“null字符串必须表示为“_”字符串”的规则(它很烦人,但这是一个传统的设计决策)。
模型:
public class OuterDTO
{
    public string TestString { get; set; }
}

public class InnerModel
{
    public string TestString { get; set; }
}

自动映射类型转换器:
public class CustomStringToStringConverter : ITypeConverter<string, string>
{
    public string Convert(ResolutionContext context)
    {
        string source = (string)context.SourceValue;

        if (source == "_")
            return null;

        return source;
    }
}

public class StringToSafirStringConverter : ITypeConverter<string, string>
{
    public string Convert(ResolutionContext context)
    {
        string source = (string)context.SourceValue;

        if (source == null)
            return "_";

        return source;
    }
}

我的测试内容是:

[TestMethod]
    public void StringToSafirStringConverterAndSafirStringToStringConverter_SafirDefaultNullStringAndNullString_ReturnsNullAndSafirNullString()
    {
        //Arrenge
        Mapper.CreateMap<string, string>().ConvertUsing(new StringToSafirStringConverter());//this has to used be DTO To Model map
        Mapper.CreateMap<string, string>().ConvertUsing(new SafirStringToStringConverter());//this has to used be Model To DTO map
        Mapper.CreateMap<InnerModel, OuterDTO>();
        Mapper.CreateMap<OuterDTO, InnerModel>();
        Mapper.AssertConfigurationIsValid();

        InnerModel expected1 = new InnerModel()
        {
            TestString = null
        };

        OuterDTO inner1 = new OuterDTO()
        {
            TestString = "_"
        };

        OuterDTO expected2 = new OuterDTO()
        {
            TestString = "_"
        };

        InnerModel outer1 = new InnerModel()
        {
            TestString = null
        };

        //Act
        InnerModel actual1 = Mapper.Map<OuterDTO, InnerModel>(inner1);
        OuterDTO actual2 = Mapper.Map<InnerModel, OuterDTO>(outer1);

        //Assert
        Assert.AreEqual(expected1.TestString, actual1.TestString);
        Assert.AreEqual(expected2.TestString, actual2.TestString);
    }

第二个断言失败了。

我需要将DTO映射到模型,将模型映射到DTO,有数百个这样的情况,并且我在整数方面也遇到了同样的问题。我该如何选择转换器来进行不同的映射?

1个回答

2

你正在声明两个从字符串到字符串的映射:

Mapper.CreateMap<string, string>().ConvertUsing(new StringToSafirStringConverter());
Mapper.CreateMap<string, string>().ConvertUsing(new SafirStringToStringConverter());

映射是不可覆盖的,因此如果您查看AutoMapper配置,您将只看到一个从字符串到字符串的映射。(即使您可以定义两个从字符串到字符串的映射,AutoMapper也无法知道使用哪个映射。)

解决此问题的方法是使用ValueResolvers定义属性映射:

Mapper.CreateMap<InnerModel, OuterDTO>()
                .ForMember(dest => dest.TestString,
                    opt => opt.ResolveUsing<StringToSafirStringResolver>()
                       .FromMember(source => source.TestString));

Mapper.CreateMap<OuterDTO, InnerModel>()
                .ForMember(dest => dest.TestString,
                    opt => opt.ResolveUsing<SafirStringToStringResolver>()
                      .FromMember(source => source.TestString));

然后您需要编写ValueResolvers,例如:
public class StringToSafirStringResolver : ValueResolver<string, string>
{
    protected override string ResolveCore(string source)
    {
        return source ?? "_";
    }
}

public class SafirStringToStringResolver : ValueResolver<string, string>
{
    protected override string ResolveCore(string source)
    {
        return source == "_" ? null : source;
    }
}

然后,您可以对整数和日期时间值执行相同的操作。


这是实际的做法,但我正在努力解决映射问题。我有两百个模型和成千上万个字段。也许我需要找到一种使用ValueResolvers的方法来解决。 - undefined
如果您的所有字符串、整数和日期时间字段都具有这些默认值,您可能可以使用反射来为您生成映射。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接