返回总目录
自定义值解析
虽然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没有任何匹配的源成员,但是我们给它添加了有效配置的自定义解析器,解析器就会对目标成员值的提供负责。
测试成功,结果如下:
自定义构造函数方法
因为我们只提供了自定义的解析器类型给AutoMapper,所以映射引擎会使用反射创建该值解析器的实例。如果我们不想要AutoMapper使用反射创建实例,我们要么直接提供一个实例,要么使用ConstruceBy方法来提供一个自定义的构造函数方法:
Mapper.CreateMap<Source, Destination>().ForMember(dest => dest.Total, opt => { opt.ResolveUsing<MyValueResolver>().ConstructedBy(()=>new MyValueResolver()); });
在映射操作期间,AutoMapper不使用反射,直接执行此回调函数。这在解析器可能需要构造函数参数或者需要通过Ioc容器构建的时候很有用。
这里不再做演示,有兴趣的小伙伴可自行研究。