AutoMapper官方文档(十二)【自定义值解析器】

尽管AutoMapper覆盖了不少目的地成员的映射场景,但有1%到5%的目标值需要一些帮助来解决。 很多时候,这个自定义的价值决议逻辑是领域逻辑,可以直接在我们的领域。 但是,如果这个逻辑仅仅涉及到映射操作,就会把我们的源类型弄乱,造成不必要的行为。 在这些情况下,AutoMapper允许为目标成员配置自定义值解析器。例如,我们可能想要在映射期间计算一个值:

public class Source
{
    public int Value1 { get; set; }
    public int Value2 { get; set; }
}

public class Destination
{
    public int Total { get; set; }
}

无论出于何种原因,我们希望Total是源值属性的总和。 由于其他原因,我们不能或不应该把这个逻辑放在我们的Source类型上。为了提供自定义的值解析器,我们需要先创建一个实现IValueResolver的类型:

public interface IValueResolver<in TSource, in TDestination, TDestMember>
{
    TDestMember Resolve(TSource source, TDestination destination, TDestMember destMember, ResolutionContext context);
}

ResolutionContext包含当前解析操作的所有上下文信息,例如源类型,目标类型,源值等。 一个示例实现:

public class CustomResolver : IValueResolver<Source, Destination, int>
{
    public int Resolve(Source source, Destination destination, int member, ResolutionContext context)
    {
        return source.Value1 + source.Value2;
    }
}

一旦我们有了我们的IValueResolver实现,我们将需要告诉AutoMapper在解析特定的目标成员时使用这个自定义的值解析器。 我们有几个选项告诉AutoMapper一个自定义的值解析器使用,包括:

    ResolveUsing<TValueResolver>
    ResolveUsing(typeof(CustomValueResolver))
    ResolveUsing(aValueResolverInstance)

在下面的例子中,我们将使用第一个选项,通过泛型告诉AutoMapper自定义的解析器类型:

Mapper.Initialize(cfg =>
   cfg.CreateMap<Source, Destination>()
     .ForMember(dest => dest.Total, opt => opt.ResolveUsing<CustomResolver>());
Mapper.AssertConfigurationIsValid();

var source = new Source
    {
        Value1 = 5,
        Value2 = 7
    };

var result = Mapper.Map<Source, Destination>(source);

result.Total.ShouldEqual(12);

尽管目标成员(Total)没有任何匹配的源成员,但指定自定义解析程序使配置有效,因为解析程序现在负责为目标成员提供值。

如果我们不关心我们的值解析器中的源/目标类型,或者想要在映射上重用它们,我们可以使用“object”作为源/目标类型:

public class MultBy2Resolver : IValueResolver<object, object, int> {
    public int Resolve(object source, object dest, int destMember, ResolutionContext context) {
        return destMember * 2;
    }
}

自定义构造函数方法

由于我们只将自定义解析器的类型提供给AutoMapper,所以映射引擎将使用反射来创建值解析器的实例。

如果我们不希望AutoMapper使用反射来创建实例,我们可以直接提供它:

Mapper.Initialize(cfg => cfg.CreateMap<Source, Destination>()
    .ForMember(dest => dest.Total,
        opt => opt.ResolveUsing(new CustomResolver())
    );

AutoMapper将使用该特定对象,在解析器可能具有构造函数参数或需要由IoC容器构造的场景中很有用。

自定义提供给解析器的源值

默认情况下,AutoMapper将源对象传递给解析器。 这限制了解析器的可重用性,因为解析器被耦合到源类型。 但是,如果我们提供跨多种类型的通用解析器,我们将AutoMapper配置为重定向提供给解析器的源值,并使用不同的解析器接口,以便我们的解析器可以使用源/目标成员:

Mapper.Initialize(cfg => {
cfg.CreateMap<Source, Destination>()
    .ForMember(dest => dest.Total,
        opt => opt.ResolveUsing<CustomResolver, decimal>(src => src.SubTotal));
cfg.CreateMap<OtherSource, OtherDest>()
    .ForMember(dest => dest.OtherTotal,
        opt => opt.ResolveUsing<CustomResolver, decimal>(src => src.OtherSubTotal));
});

public class CustomResolver : IMemberValueResolver<object, object, decimal, decimal> {
    public decimal Resolve(object source, object destination, decimal sourceMember, decimal destinationMember, ResolutionContext context) {
       //逻辑在这里
    }
}

将键值对传递给Mapper

在调用映射时,可以使用键值传递额外的对象,并使用自定义解析器从上下文获取对象。

Mapper.Map<Source, Dest>(src, opt => opt.Items["Foo"] = "Bar");

这是如何设置这个自定义解析器的映射

Mapper.CreateMap<Source, Dest>()
    .ForMember(d => d.Foo, opt => opt.ResolveUsing((src, dest, destMember, res) => res.Context.Options.Items["Foo"]));

ForPath

ForMember相似,从6.1.0开始有ForPath

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值