AutoMapper总结

  AutoMapper是一个对象和对象间的映射器。对象与对象的映射是通过转变一种类型的输入对象为一种不同类型的输出对象工作的。让AutoMapper有意思的地方在于它提供了一些将类型A映射到类型B这种无聊的事情的有趣惯例。只要类型B遵守AutoMapper已经建立的惯例,大多数情况下,映射两种类型零配置就可以了。

  首先,需要有source(源)和destination(目标)类型。默认情况下AutoMapper 只要Destination类型的成员名字与Source类型的成员名字匹配(成员名称不区分大小写),并且成员类型相同就直接会将值传递给Destination类型。

  AutoMapper只要做一次初始化就可以使用,因此最好的配置代码的地方应该在应用启动时。

初始化处理(Profile实例)

  Initialize():实例化Mapper对象

  CreateMap():创建映射关系 Ex:Mapper.Initialize(cfg=>{ cfg.CreateMap<Source, Dest>(); });

  AddProfile:使用配置文件创建映射关系 Ex: Mapper.Initialize(cfg=>{cfg.AddProfile<MyProfile>(); });对于MyProfile对象需要继承Profile对象,并重写 Configure()方法。

映射前后操作:  

  BeforeMap(): 在映射前执行的处理。Ex:BeforeMap((src, dest) => src.Value = src.Value + 1)

  AfterMap(): 在映射后执行的处理。 Ex:AfterMap((src, dest) => src.Name = "FengTest")

条件映射

  ForMember():对于映射中成员的处理。

  Condition():对于条件映射处理,满足该条件才会给目标成员赋值。

  Ex:ForMember(dest => dest.Value, opt => opt.Condition(src => src.Value > 0 && src.Value < 130));

命名惯例

  SourceMemberNamingConvention :源类型成员命名规则

  Ex: cfg.SourceMemberNamingConvention = new LowerUnderscoreNamingConvention(); //下划线命名法

  cfg.DestinationMemberNamingConvention :目标类型成员命名规则

  Ex: cfg.DestinationMemberNamingConvention = new PascalCaseNamingConvention(); //帕斯卡命名法

替换字符

  ReplaceMemberName():将源成员名称中的字符替换成新的字符此方法必须在CreateMap之前

  Ex: cfg.ReplaceMemberName("Tool", "Car");

前后缀

  RecognizePrefixes():识别源成员名称中的前缀字符 Ex: cfg.RecognizePrefixes("P");

  RecognizePostfixes():识别源成员名称中的后缀字符 Ex: cfg.RecognizePostfixes("L");

  此方法必须在CreateMap之前,并且默认是识别“Get”字符前缀

  ClearPrefixes(): 清除前缀 Ex:cfg.CliearPrefixes()

全局过虑

  ShouldMapField : 字段的映射条件 Ex: cfg.ShouldMapField = field => false; //不映射任何字段

  ShouldMapProperty:属性的映射条件

  Ex:cfg.ShouldMapProperty = pro => pro.GetMethod != null && pro.GetMethod.IsPrivate;

  此方法必须是源类型与目标类型都满足该条件才能映射处理,默认情况下AutoMapper只对公共成员做映射,如果想识别Private/internal,可以修改这两个属性值。

构造函数

  默认情况下,当目标类型的构造函数中的参数名称、类型与源类型成员的名称、类型相同,AutoMapper会自动映射。

  当构造函数的参数名称与目标成员的名称不同时,可使用ForCtorParam()方法指定。

  ForCtorParam() : 对构造函数参数映射Ex:ForCtorParam("age1",user=>user.MapFrom(src=>src.Age))

条件对象映射器

  AddConditionalObjectMapper():符合条件后两个类型自动映射。

  Ex:cfg.AddConditionalObjectMapper().Where((s, d) => d.Name == s.Name + "Dto");

成员配置

  AddMemberConfiguration():配置文件, 配置中的东西都是以该方法开始的。

  Ex:cfg.AddMemberConfiguration().AddMember<NameSplitMember>(); 默认配置

  AddName<ReplaceName>(r => r.AddReplace("B", "A")) 替换字符

  AddName<PrePostfixName>(p => p.AddStrings(pr => pr.Prefixes, "Get", "get"))识别前缀

  AddName<PrePostfixName>(p => p.AddStrings(pr => pr.Postfixes, "Set", "set"))识别后缀

  AddName<SourceToDestinationNameMapperAttributesMember>() 特性支持

  [MapToAttribute("Phone")] :特性,匹配基于给定名称的属性

  AutoMapper默认值:AddMemberConfiguration().AddMember<NameSplitMember>()

  .AddName<PrePostfixName>(_ => _.AddStrings(p => p.Prefixes,"Get"))

  .AddName<SourceToDestinationNameMapperAttributesMember>();

  AddName和AddMember中的每个类型都是基于ISourceToDestinationNameMapper和IChildMemberConfiguration接口的。也可以创建自己的类通过Lambda语句参数来配置属性,因此你可以微调AutoMapper如何解析属性映射。

自定义类型转换 

  在作对象之间的转换时,有些属性的类型是不能直接转换的,但经过验证,默认可以将string类型映射为int和DateTime类型。  

方法一:

ConvertUsing() :使用映射的方式

Ex: cfg.CreateMap<Source, Destination>().ConvertUsing(s =>

{

var d = new Destination();

d.Value1 = System.Convert.ToInt32(s.Value1);

d.Value2 = System.Convert.ToDateTime(s.Value2);

d.Value3 = Type.GetType(s.Value3);

return d;

});

方法二:

定一个类,该类型需要继承 ITypeConverter泛型接口,源类型和目标类型,并且实现Convert方法

Ex: public class CustomTypeConverter : ITypeConverter<Source, Destination>

然后在Mapper.Initialize的时候使用ConvertUsing的泛型方法

Ex: cfg.CreateMap<Source, Destination>().ConvertUsing<CustomTypeConverter>();

方法三:

定一个类,该类型需要继承ITypeConverter泛型接口,源基础类型和目标基础类型,并且实现Convert方法

Ex:public class TypeConverter : ITypeConverter<string, Type>

然后在Mapper.Initialize的时候创建该基类型的映射,该方式在Mapper全局都是有效的

Ex: Mapper.Initialize(cfg => {

cfg.CreateMap<string, Type>().ConvertUsing<TypeConverter>();

cfg.CreateMap<Source, Destination>();

});

自定义值解析

  虽然AutoMapper覆盖了很大一部分目标成员的映射场景,但还是有一部分需要自定义处理。因是对目标类的中的某一个属性的赋值处理,因些会用到ForMember()方法。使用ResolveUsing() :指定赋值的方式。

方法一:

Ex:cfg.CreateMap<Source, Destination>().ForMember(dest => dest.Total, opt => {

opt.ResolveUsing(s =>

{

var destination = new Destination();

destination.Total = s.Value1 + s.Value2;

return destination.Total;

}); });

方法二:

创建一个类,并实现IValueResolver方法

Ex: public class MyValueResolver : IValueResolver<Source, Destination, int>

然后在创建映射关系的时候为其更改赋值方式

Ex: cfg.CreateMap<Source, Destination>().ForMember(dest => dest.Total, opt => { opt.ResolveUsing<MyValueResolver>(); });

Dynamic和ExpandoObject映射

Dynamic动态创建对象 Ex:dynamic dynamicObj = new ExpandoObject();//ExpandoObject对象包含可在运行时动态添加或移除的成员.

虽然Dynamic为动态对象不有办法创建映射关系,但必须先对Mapper实例化,才能使用。

Ex: public static ExpandoObject DynamicAndExpandoObject()

{

Mapper.Initialize(cfg=> { });

dynamic dynamicObj = new ExpandoObject();

dynamicObj.Age = 12;

dynamicObj.Name = "Feng测试";

Person person = Mapper.Map<Person>(dynamicObj);

Console.WriteLine("person.Age={0},Name={1}", person.Age, person.Name);

dynamic dynamicSecond = Mapper.Map<ExpandoObject>(person);

dynamicSecond.Address = "北京";

Console.WriteLine("dynamicObj.Age={0},Name={1},Address={2}", dynamicSecond.Age, dynamicSecond.Name, dynamicSecond.Address);

return dynamicSecond;

}

扁平化

性相匹配。 如果对目标类型上的任何属性,方法或以“Get”为前缀的方法不存在源类型上,则AutoMapper会将目标成员名称拆分为单个单词(遵循帕斯卡(PascalCase)拼写法约定)。

Ex:public class Order

{

public Customer Customer { get; set; }

public decimal GetTotal()

{

return 100M;

}

}

public class Customer

{

public string Name { get; set; }

}

public class OrderDto

{

public string CustomerName { get; set; }

public decimal Total { get; set; }

}

当使用 Order与OrderDto映射时,Total属性匹配到了Order上的GetTotal方法。CustomerName属性匹配到了Order上的Customer.Name属性。总之,只要合适地命名目标类型属性,我们就不必配置单独的属性匹配。

List和数组

AutoMapper只要求元素类型的配置而不要求可能会用到的任何数组或者list类型。因此的创建映射的配置中,只是配置类型之间的映射,而不需要设计任何集合类型。

具体来说,支持的源集合类型包括:

    • IEnumerable
    • IEnumerable<T>
    • ICollection
    • ICollection<T>
    • IList
    • IList<T>
    • List<T>
    • Arrays

集合中的多态元素类型

AutoMapper支持多态数组和集合,因此如果发现派生的源或者目标类型就会自动转换,但创建的时候需要声明。

Ex:cfg.CreateMap<ParentSource, ParentDestination>().Include<ChildSource, ChildDestination>();

但也需要声明子类型的映射,因为AutoMapper“猜不出”具体是哪个子类型

Ex:cfg.CreateMap<ChildSource, ChildDestination>();

也可以声明子类型的映射,再包含基类型,但也要再声明基类型的映射

Ex:cfg.CreateMap<ParentSource, ParentDestination>();

cfg.CreateMap<ChildSource, ChildDestination>().IncludeBase<ParentSource, ParentDestination>();

继承映射属性

对一个属性的映射有多种方式,下面是这些源的优先级

    • 显式映射 (使用.MapFrom())
    • 继承的显式映射
    • 默认的映射 (通过默认匹配的属性)
    • 忽略的属性映射

Ex: cfg.CreateMap<Order, OrderDto>().Include<PCOrder, OrderDto>().Include<MobileOrder, OrderDto>().ForMember(o => o.Referrer, m => m.Ignore());//这里配置了忽略目标属性Referrer的映射

虽然在配置中忽略了Referrer的映射处理,但还是会将源类型中的Referrer属性值给到目标。这就是优先级的问题。这就是因为 默认的映射比忽略的映射优先级高。

Null值替换

当源类型中某个属性为空值的时候在映射到目标类型时,目标类型的该属性可以设置一个默认值

NullSubstitute(),如果源属性为空,则用该方法设置默认值。

Ex: cfg.CreateMap<Person, PersonInfo>().ForMember(dest => dest.Title, opt => opt.NullSubstitute("屌丝"));

开放泛型

所谓的开放泛型是指 源类型为一个泛型类,目标类型也是一个泛型类型。在创建两个泛型类的映射时,中的泛型不需要填写。

Ex: cfg.CreateMap(typeof(Source<>), typeof(Destination<>));

也可以使用一个泛型转换器来进行处理。

Ex: cfg.CreateMap(typeof(Source<>),typeof(Destination<>))

.ConvertUsing(typeof(Converter<>));

投影

当把一个源值投影到一个不精准匹配源结构的目标值时,必须指明成员映射定义。

MapFrom() :指定源类型属性的值。

Ex: .ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.Date.Date))

学习来源:http://www.cnblogs.com/farb/p/4932692.html

代码地址:https://github.com/HaiFengNet/AutoMapperDemo

 

时间: 2024-12-13 08:44:26

AutoMapper总结的相关文章

使用AutoMapper实现Dto和Model的自由转换(上)

在实际的软件开发项目中,我们的"业务逻辑"常常需要我们对同样的数据进行各种变换.例如,一个Web应用通过前端收集用户的输入成为Dto,然后将Dto转换成领域模型并持久化到数据库中.另一方面,当用户请求数据时,我们又需要做相反的工作:将从数据库中查询出来的领域模型以相反的方式转换成Dto再呈现给用户.有时候我们还会面临更多的数据使用需求,例如有多个数据使用的客户端,每个客户端都有自己对数据结构的不同需求,而这也需要我们进行更多的数据转换. 频繁的数据转换琐碎而又凌乱,很多时候我们不得不:

使用AutoMapper实现Dto和Model的自由转换(中)

在上一篇文章中我们构造出了完整的应用场景,包括我们的Model.Dto以及它们之间的转换规则.下面就可以卷起袖子,开始我们的AutoMapper之旅了. [二]以Convention方式实现零配置的对象映射 我们的AddressDto和Address结构完全一致,且字段名也完全相同.对于这样的类型转换,AutoMapper为我们提供了Convention,正如它的官网上所说的: 引用 AutoMapper uses a convention-based matching algorithm to

使用AutoMapper实现Dto和Model的自由转换(下)

书接上文.在上一篇文章中我们讨论了使用AutoMapper实现类型间1-1映射的两种方式--Convention和Configuration,知道了如何进行简单的OO Mapping.在这个系列的最后一篇文章我想基于我们的需求讨论一些中级别的话题,包括:如何实现类型体型之间的映射,以及如何为两个类型实现多个映射规则. [四]将一个类型映射为类型体系 先回顾一下我们的Dto和Model.我们有BookDto,我们有Author,每个Author有自己的ContactInfo.现在提一个问题:如何从

对象映射工具AutoMapper介绍

AutoMapper是用来解决对象之间映射转换的类库.对于我们开发人员来说,写对象之间互相转换的代码是一件极其浪费生命的事情,AutoMapper能够帮助我们节省不少时间. 一. AutoMapper解决了什么问题? 要问AutoMapper解决了什么问题? 难道不是对象映射转换的问题吗? 当然是,不过我们可以问深入一些,为什么项目中会出现大量的对象映射转换?(以下对于非MVC项目也适用) 在现代的软件开发中,项目的层级更加的细分,而不同层级之间对于对象的需求是有区别的,这就需要在不同层级间传递

0.AutoMapper

AutoMapper是基于约定的对象 - 对象映射器.AutoMapper使用流畅的配置API来定义对象 - 对象映射策略.AutoMapper使用基于约定的匹配算法来匹配源到目标值.AutoMapper面向模型投影场景,将复杂的对象模型变成DTO和其他简单对象,这些对象的设计更适合于序列化,通信,消息传递或简单的域和应用程序层之间的防腐层. 1.扁平化 2.投影 3.配置验证 4.列表和数组 5.嵌套映射 6.自定义类型转换器 7.自定义值解析器 8.空替换 9.映射操作之前和之后 10.依赖

AutoMapper的介绍与使用(二)

AutoMapper的匹配 1,智能匹配 AutoMapper能够自动识别和匹配大部分对象属性: 如果源类和目标类的属性名称相同,直接匹配,不区分大小写 目标类型的CustomerName可以匹配源类型的Customer.Name 目标类型的Total可以匹配源类型的GetTotal()方法 2,自定义匹配 Mapper.CreateMap<CalendarEvent, CalendarEventForm>()                                          

升级AutoMapper后遇到的“Missing map”与“Missing type map configuration or unsupported mapping”问题

前几天发现 AutoMapper 3.3 的一个性能问题(详见:遭遇AutoMapper性能问题:映射200条数据比100条慢了近千倍),于是将 AutoMapper 升级至最新的 5.1.1 看是否也存在这个性能问题. 升级中想当然地将之前的map配置代码: Mapper.CreateMap<AEntity, ADto>(); Mapper.CreateMap<BEntity, BDto>(); 改为: Mapper.Initialize(cfg => cfg.Create

遭遇AutoMapper性能问题:映射200条数据比100条慢了近千倍

今天遇到了AutoMapper的一个性能问题,使用的是AutoMapper的Project特性,AutoMapper版本是3.3.0,代码如下: return await _repository .GetByStartId(startIngId, itemCount) .Project() .To<TDto>() .ToListAsync(); 当获取包含200条数据的列表时,竟然超过5秒. GetDocs(3000, 200) 6304ms GetDocs(3000, 200) 5822ms

分享一个简单程序(webApi+castle+Automapper+Ef+angular)

前段时间在周末给朋友做了一个小程序,用来记录他们单位的一些调度信息(免费,无版权问题).把代码分享出来.整个程序没有做任何架构.但是麻雀虽小,用到的技术也没少.WebApi+Castle+AutoMapper+Ef+angular,日志记录Log4net.初学者可以学习借鉴,虽然做的比较仓促,但是自我感觉代码写的还是比较规范. git地址:https://git.oschina.net/richieyangs/TaskRecord.git 只需修改web.config中的连接字符串,Ctrl+F

无名小卒AutoMapper的大显神威

最近在做gxpt(高校平台的项目),前台框架用的是MVC,后台用的是EF+WCF.怎么突然说出来感觉很高大上的样子.呵呵!但是在写代码的时候就发现一个问题.就是在EF中的实体(Model)跟前台需要的实体(DTO数据传输对象)是不一样的,所以就需要转换一下.一开始也没多想,可能是被做项目的热情冲昏了头脑,写多少代码都不觉得类,所以每当需要转换的时候就一个一个的写!给大家看一个例子! <span style="font-size:18px;">namespace testau