首先,先创建一个控制台项目,引用AutoMapper程序集,创建三个类User,UserDto,UserMappingProfile,下面的知识点的演示都以此项目为基础,代码分别如下:
namespace MiddleAutoMapper{ public class User { public User() { } public int Age { get; set; } }}
namespace MiddleAutoMapper{ public class UserDto { public UserDto(int age) { this.age = age; } private int age; public int Age { get { return age; } } }}
namespace MiddleAutoMapper{ public class UserMappingProfile:Profile { protected override void Configure() { //添加配置方法 } }}
namespace MiddleAutoMapper{ class Program { static void Main(string[] args) { //初始化配置 Mapper.Initialize(cfg => { cfg.AddProfile<UserMappingProfile>(); }); var u1 = Mapper.Map<UserDto>(new User() {Age = 23}); Console.WriteLine(u1.Age); Console.Read(); } }}
构造
AutoMapper可以根据源类型的成员映射到目标类型的构造函数里:
在UserMappingProfile文件中添加配置方法代码:
Mapper.CreateMap<User, UserDto>();
因为AutoMapper会对大小不敏感(case insensitive),所以会将User的Age属性通过UserDto的构造函数进行映射,并对UserDto的私有字段赋值,最后通过公有的Age属性打印出来。
但是,这是在UserDto的构造函数参数名和User的Age属性名相同的情况下,如果不相同呢?比如,将构造函数的参数名改为age1呢?
发现报错了,所以我们要修改配置方法,如下:
Mapper.CreateMap<User, UserDto>().ForCtorParam("age1", opt => opt.MapFrom(src => src.Age));
解释一下ForCtorParam()方法,看名字是针对构造函数参数的,官方解释该方法的作用是对于个别的构造函数参数进行自定义配置。该方法有两个参数,第一个是string类型的构造函数参数名,第二个是一个参数选项Action方法。
我上面这句代码的意思就是说将目标类型的构造函数的参数“age1”从源类型的Age属性进行映射。
容器
AutoMapper支持使用静态服务地址构造自定义值格式化器,自定义值解析器和自定义类型转换器(后面会说):
Mapper.Initialize(cfg => { cfg.ConstructServicesUsing(ObjectFactory.GetInstance); cfg.CreateMap<Source, Destination>(); });
或者在基于实例的容器使用的动态服务地址(包括子容器或嵌套容器):
var dest = Mapper.Map<Source, Destination>(new Source { Value = 15 }, opt => opt.ConstructServicesUsing(childContainer.GetInstance));
惯例
条件对象映射器
条件对象映射器基于源类型和目标类型之间的条件生成新的类型映射。
首先将User和UserDto的构造函数去掉,配置文件中Configure方法中的代码只写入下面这句代码:
AddConditionalObjectMapper().Where((s, d) => d.Name == s.Name + "Dto");
解释:如果目标类型的名称等于源类型的名称加上“Dto”,那么就生成一个源类型和目标类型的对象映射器。添加了这一句,就不需要CreateMap方法了。
在这连个类中加入属性:
public string UserName { get; set; }
可见,即使没有使用CreateMap方法,同样映射成功了。
成员配置
成员配置就像配置(Configuration)一样,但是你可以完全控制用什么和不用什么。
Mapper.Initialize(cfg => { cfg.AddMemberConfiguration(); });
AddMemberConfiguration()方法以空白状态开启。默认情况下,应用到Configuration中的所有东西都是从这里开始的。
- 命名惯例
AddMemberConfiguration().AddMember<NameSplitMember>();
这句代码可以获得默认的命名惯例。也可以通过参数传递Lambda重写源和目标的命名惯例。如果你没设置任何东西,那么AutoMapper会使用DefaultMember,它会只检测用到的属性名称。PS:如果没设置这个,那么对象的扁平化(Flattening)就不可用。 - 替换字符
AddMemberConfiguration().AddName<ReplaceName>(_ => _.AddReplace("Ä", "A").AddReplace("í", "i"));
- 识别前后缀
AddMemberConfiguration().AddName<PrePostfixName>(_ => _.AddStrings(p => p.Prefixes, "Get", "get").AddStrings(p => p.DestinationPostfixes, "Set"));
- 特性支持
AddMemberConfiguration().AddName<SourceToDestinationNameMapperAttributesMember>();
这个默认总是开启的。它会寻找含有“SourceToDestinationMapperAttribute”特性的属性所在的实例,并且调用用户定义的isMatch函数类找出匹配的成员。
MapToAttribute是特性之一,它可以匹配基于给定名称的属性。
在User类中加入代码:在UserDto中加入代码:[MapToAttribute("Phone")]public string Tel { get; set; }
在Main方法中加入代码:public string Phone { get; set; }
执行结果如下:var u3 = Mapper.Map<UserDto>(new User() {Tel = "123456"});Console.WriteLine(u3.Phone);//123456
- 获取AutoMapper默认值
AddMemberConfiguration().AddMember<NameSplitMember>().AddName<PrePostfixName>(_ => _.AddStrings(p => p.Prefixes, "Get"))
如果没使用AddMemberConfiguration方法,那么这就是Configuration设置的默认值。
扩展性
AddName和AddMember中的每个类型都是基于ISourceToDestinationNameMapper和IChildMemberConfiguration接口的。也可以创建自己的类通过Lambda语句参数来配置属性,因此你可以微调AutoMapper如何解析属性映射。
配置文件
配置文件可以加到Profile和ConfigurationStore中。
每一个配置文件都独立于另外一个,且不会共享任何条件。如果一个映射是从一个配置文件(profile)中的AddConditionalObjectMapper生成的,那么可以使用此配置文件的AddMemberConfigurations来解析属性映射。