你的位置:首页 > ASP.net教程

[ASP.net教程]AutoMapper(四)


返回总目录


自定义值解析

虽然AutoMapper覆盖了相当一部分目标成员的映射场景,但是还有 1-5%的目标值需要解析处理一下。很多时候,自定义的值解析是可以放在领域层的领域逻辑。然而,如果该逻辑只是和映射操作有关的话,那它就会应为一些不必要的行为使得源类型很凌乱。这种场合,AutoMapper允许我们为目标成员配置自定义的值解析器。

举个栗子,有两个类Source和Destination,定义如下:

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

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

我们想要在映射期间产生一个计算的值,也就是说,在Source到Destination映射的过程中,把Source的两个属性值加起来赋给Destination的属性。由于某些原因,我们不能把这个逻辑放到源类型里,为了提供一个自定义值解析器,我们首先需要创建一个实现了IValueResolver:

public class MyValueResolver : IValueResolver{  public ResolutionResult Resolve(ResolutionResult source)  {    //TODO:实现逻辑  }}

ResolutionContext包含了当前解析操作的所有上下文信息,如源类型,源值等等。大多数场合,我们不需要这个更高级的接口。另一种方法,我们可以实现抽象类ValueResolver<TSource, TDestination>:

public class MyValueResolver : ValueResolver<Source, int>{  protected override int ResolveCore(Source source)  {    return source.Value1 + source.Value2;  }}

现在,我们已经实现了自己的值解析器,接下来就需要告诉AutoMapper,当解析一个特定的目标成员时,要使用这个自定义的值解析器。有3中方式告诉AutoMapper如何使用自定义解析器,包括:

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

接下来,我们就开始使用自己的值解析器:

class Program{  static void Main(string[] args)  {    Mapper.CreateMap<Source, Destination>().ForMember(dest => dest.Total, opt =>    {      opt.ResolveUsing<MyValueResolver>();    });    var src = new Source {Value1 = 3, Value2 = 5};    var destObj= Mapper.Map<Destination>(src);    Console.WriteLine("destObj.Total={0}", destObj.Total);    Console.Read();  }}

虽然目标成员Total没有任何匹配的源成员,但是我们给它添加了有效配置的自定义解析器,解析器就会对目标成员值的提供负责。

测试成功,结果如下:

image

自定义构造函数方法

因为我们只提供了自定义的解析器类型给AutoMapper,所以映射引擎会使用反射创建该值解析器的实例。如果我们不想要AutoMapper使用反射创建实例,我们要么直接提供一个实例,要么使用ConstruceBy方法来提供一个自定义的构造函数方法:

Mapper.CreateMap<Source, Destination>().ForMember(dest => dest.Total, opt =>{  opt.ResolveUsing<MyValueResolver>().ConstructedBy(()=>new MyValueResolver());});

在映射操作期间,AutoMapper不使用反射,直接执行此回调函数。这在解析器可能需要构造函数参数或者需要通过Ioc容器构建的时候很有用。

这里不再做演示,有兴趣的小伙伴可自行研究。