Automapper:对相同的类型使用类型转换器和值解析器



我有很多dto必须映射到我的域的对象。通常,到货币值的映射需要应用舍入规则。这适用于95%以上的情况,但我有一些数据需要不同的舍入规则。我计划做下一个:

1)为一般舍入规则创建一个typeeconverter,该规则适用于此类型转换的默认值

2)为特殊的四舍五入情况创建一个ValueResolver,并指定它们在成员映射中的使用。

我创建了一个测试用例来尝试我的方法,但是在将ValueResolver应用于特殊情况之后,AutoMapper使用TypeConverter使用一般规则来获得最终值。

这是一个错误的方法?也许我遗漏了什么?

然后测试用例:

[TestFixture]
public class RoundingTests
{
    public class MoneyConverter : ITypeConverter<decimal, decimal>
    {
        public decimal Convert(ResolutionContext context)
        {
            return Math.Round((decimal)context.SourceValue, 2, MidpointRounding.AwayFromZero);
        }
    }
    public class Money12Resolver : ValueResolver<decimal, decimal>
    {
        protected override decimal ResolveCore(decimal source)
        {
            return Math.Round(source, 12, MidpointRounding.AwayFromZero);
        }
    }
    public class PercentResolver : ValueResolver<decimal, decimal>
    {
        protected override decimal ResolveCore(decimal source)
        {
            return Math.Round(source, 4, MidpointRounding.AwayFromZero);
        }
    }
    internal class Source
    {
        public decimal Price { get; set; }
        public decimal Percent { get; set; }
        public decimal Prorate { get; set; }
    }
    internal class Destination
    {
        public decimal Price { get; set; }
        public decimal Percent { get; set; }
        public decimal Prorate { get; set; }
    }
    [Test]
    public void MappingTest()
    {
        Mapper.CreateMap<decimal, decimal>().ConvertUsing<MoneyConverter>();
        Mapper.CreateMap<Source, Destination>()
            .ForMember(d => d.Percent, o => o.ResolveUsing<PercentResolver>().FromMember(s => s.Percent))
            .ForMember(d => d.Prorate, o => o.ResolveUsing<Money12Resolver>().FromMember(s => s.Prorate));
        Mapper.AssertConfigurationIsValid();
        var source = new Source
                     {
                         Price = 12345.6789m,
                         Percent = 0.123456m,
                         Prorate = 123.123451234512345m
                     };
        var convertion = Mapper.Map<Destination>(source);
        Assert.That(convertion, Is.Not.Null);
        Assert.That(convertion.Price, Is.EqualTo(12345.68m));
        Assert.That(convertion.Percent, Is.EqualTo(0.1235m));
        Assert.That(convertion.Prorate, Is.EqualTo(123.123451234512m));
    }
}
测试结果:

  Expected: 0.1235m
  But was:  0.12m

您告诉AutoMapper使用您的MoneyConverter转换所有十进制->十进制映射。事实上,AutoMapper确实会使用您的解析器(设置一个断点来查看),但是解析的结果是一个十进制值,然后由您应用的MoneyConverter使用。

注: 这似乎是AutoMapper的设计;

并非所有的十进制属性都表示金钱,因此您可能需要采用不同的方法。还要问你自己,你是纯粹为了表示值而舍入,还是你可能会失去你想在这些领域对象中保留的精度?如果确实需要舍入,可以显式地为每个成员设置一个解析器,完全跳过转换器。或者,您可以忽略异常成员,并使用.ConstructUsing(...)来处理它。

但是由于您最初的问题涉及到使用同一类型的解析器和转换器,这里有一种方法可以使其工作:


基本上,我们希望转换器跳过某些属性的默认转换。我们不能通过配置来做,所以我们必须在运行时做。假设您可以访问Destination类,只需使用自定义属性将属性装饰为非默认行为。

class PercentAttribute : Attribute
{   }
class Destination
{
    [Percent]
    public decimal Percent { get; set; }
    ...
}

在你的转换器中,你可以寻找[Percent]的属性,并返回源值,它来自你的PercentResolver

public class MoneyConverter : ITypeConverter<decimal, decimal>
{
    public decimal Convert(ResolutionContext context)
    {
        var propInfo = context.PropertyMap.DestinationProperty.MemberInfo;
        bool isPercent = propInfo.GetCustomAttributes(typeof(PercentAttribute), true).Any();
        if (isPercent) return (decimal)context.SourceValue;
        return Math.Round((decimal)context.SourceValue, 2, MidpointRounding.AwayFromZero);
    }
}

如果您打算采用这种方法,只需让转换器基于属性进行Money、Percent和Money12转换,并完全跳过解析器。

相关内容

  • 没有找到相关文章

最新更新