在两个不同的类型对象之间传输数据,通常我们会用DTOs(数据传输对象),AutoMapper就是将一个对象自动转换为另一个对象的技术

背景

一些orm框架,在用到Entity的时候有一些开源代码用到了automapper(如:nopcommence),将数据对象转成DTO。比如在ORM中,与数据库交互用的Model模型是具有很多属性变量方法神马的。而当我们与其它系统(或系统中的其它结构)进行数据交互时,出于耦合性考虑或者安全性考虑或者性能考虑(总之就是各种考虑),我们不希望直接将这个Model模型传递给它们,这时我们会创建一个贫血模型来保存数据并传递。什么是贫血模型?贫血模型(DTO,Data Transfer Object)就是说只包含属性什么的,只能保存必须的数据,没有其它任何的多余的方法数据什么的,专门用于数据传递用的类型对象。在这个创建的过程中,如果我们手动来进行,就会看到这样的代码:

A a=new A();
a.X1=b.X1;
a.X2=b.X2;
...
...
...
return a; 太麻烦

此时,AutoMapper可以发挥的作用就是根据A的模型和B的模型中的定义,自动将A模型映射为一个全新的B模型。(不用一个属性一个属性的赋值)

好处:

1、 db或者模型 增加字段时,只需在DTO内部增加映射,赋值代码无需修改

2、隔离,前端收集各参数,不用管后端定义的模型。前后端才用AutoMapper来做转换。

使用

Nuget引用:AutoMapper    版本不一样,里面的很多方法有些不一样

AutoMapper是基于约定的,因此在实用映射之前,我们需要先进行映射规则的配置。

我们要做的只是将要映射的两个类型告诉AutoMapper(调用Mapper类的Static方法CreateMap并传入要映射的类型):

Mapper.Initialize(cfg => { cfg.CreateMap<StudentEntity, StudentOutput>(); });

也可以将实体类 放在配置文件MapperProfile中

Mapper.Initialize(cfg => {

cfg.AddProfile<MapperProfile>();

cfg.AddProfile<ProxyAdapterProfile>();  //可增加多个

});

注意:多次调用 Mapper.Initialize() 只有最后一次生效。所以只能用一个Mapper.Initialize。

【AutoMapper.7.0.1】

class MapperProfile : Profile

  {

public MapperProfile()

        {

            CreateMap<StudentEntity, StudentOutput>();

            var map = CreateMap<UploadResponseBase, UploadResult>();

            //字段名称不一致,一次直接定义好所有字段的映射规则

            map.ConvertUsing(s => new UploadResult

            {

                IsSuccess = s.success,

                FileUrl = s.clientUrl,

                ErrorMessage = s.rawFileName

                , datetimeStr = (s.datetime).ToString(),

            });

        }

}

【AutoMapper 4.2.1.0】

AutoMapper使用ForMember来指定每一个字段的映射规则:

protected override void Configure()

        {

            var mapResponst = CreateMap<Response, CResponse>();

            mapResponst.ForMember(dest => dest.departure_date, opt => opt.MapFrom(src => src.DepartureDate.ToString("yyyy-MM-dd HH:mm:ss")))

                                  .ForMember(dest => dest.ticket_price, opt => opt.MapFrom(src => src.TicketPrice));

            var mapContacts = CreateMap<CContacts, PassengerInputEntity>();

            mapContacts.ForMember(dest => dest.FirstName, opt => opt.MapFrom(src => src.First_Name))

            .ForMember(dest => dest.LastName, opt => opt.MapFrom(src => src.Last_Name))

            .ForMember(dest => dest.AreaCode, opt => opt.MapFrom(src => src.Area_Code));

        }

然后就可以交给AutoMapper帮我们搞定一切了: 

//实例化实体List

List<StudentEntity> StudentList = new List<StudentEntity>();

//模拟数据

StudentList.Add(new StudentEntity

{

Id = 1,

Age = 12,

Gander = "boy",

Name = "WangZeLing",

Say = "Only the paranoid survive",

Score = 99M

});

//AuotMapper具体使用方法 将List<StudentEntity>转换为List<StudentOutput>

List<StudentOutput> Output = Mapper.Map<List<StudentOutput>>(StudentList);

Output.ForEach(output => Console.WriteLine(string.Format("name:{0},say:{1},score:{2}", output.Name, output.Say, output.Score)));

解释

1、 AutoMapper给我们提供的Convention或Configuration方式并不是“异或的”,我们可以结合使用两种方式,为名称不同的字段配置映射规则,而对于名称相同的字段则忽略配置。

2、 在映射具有相同字段名的类型时,会自动转换

3、 不相同名称的属性则需要 指定映射字段,设置ConvertUsing或者ForMember..

4、 值为空的属性,AutoMapper在映射的时候会把相应属性也置为空

5、 如果传入一个空的AddressDto,AutoMapper也会帮我们得到一个空的Address对象。 

Address address = Mapper.Map<AddressDto,Address>(null);

6、不需要映射的属性可以用Ignore忽略。【if有验证 目标类中的所有属性是否都被映射 时】

使用Ignore方法:

Mapper.CreateMap<Entity.Source, Entity.Destination>()

.ForMember(dest => dest.SomeValuefff, opt =>

{

opt.Ignore();

});

最佳实践

这段内容将讨论AutoMapper的规则写在什么地方的问题。

在上一段中,我们已经知道了如何使用AutoMapper进行简单的对象映射,但是,在实际的项目中,我们会有很多类进行映射(从Entity转换为Dto,或者从Entity转换为ViewModel等),这么多的映射如何组织将成为一个问题。

首先我们需要定义一个Configuration.cs的类,该类提供AutoMapper规则配置的入口,它只提供一个静态的方法,在程序第一次运行的时候调用该方法完成配置。

当有多个Profile的时候,我们可以这样添加:

public class Configuration

{

public static void Configure()

{

Mapper.Initialize(cfg =>

{

cfg.AddProfile<Profiles.SourceProfile>();

cfg.AddProfile<Profiles.OrderProfile>();

cfg.AddProfile<Profiles.CalendarEventProfile>();

});

}

}

在程序运行的时候,只需要调用Configure方法即可。

了解了这些实现以后,我们可以再项目中添加AutoMapper文件夹。

Configuration为我们的静态配置入口类;Profiles文件夹为我们所有Profile类的文件夹。如果是MVC,我们需要在Global中调用:

AutoMapper.Configuration.Configure();

问题:Missing type map configuration or unsupported mapping

重现:本地调试直接打开出错的页面,调试发现是ok的;然后先打开用到了mapper所在控制器对应的页面,再去打开出错的页面,是报错的。

从 GitHub 上签出 AutoMapper 的源代码一看 Mapper.Initialize() 的实现,恍然大悟。

public static void Initialize(Action<IMapperConfigurationExpression> config)

{

Configuration = new MapperConfiguration(config);

Instance = new Mapper(Configuration);

}

原来每次调用 Mapper.Initialize() 都会创建新的 Mapper 实例,也就是多次调用 Mapper.Initialize() 只有最后一次生效。

切记不要多处调用Mapper.Initialize()。

优化方法:【写一个工具类】

需程序集:AutoMapper

/// <summary>

    ///     优化AutoMap映射工具,解决AutoMap只能Initialize一次的问题

    /// </summary>

    public class AutoMapperManager

    {

        /// <summary>

        /// 存储所有的profile

        /// </summary>

        static ConcurrentBag<Profile> Profiles;

        static AutoMapperManager()

        {

            Profiles = new ConcurrentBag<Profile>();

        }

        /// <summary>

        /// 新增Profile,必須放在靜態構造函數里

        /// </summary>

        /// <param name="profile"></param>

        public static void AddProfile(Profile profile)

        {

            Profiles.Add(profile);

        }

        /// <summary>

        /// 初始化,可以多次调用,同时之前的Profile也会生效

        /// </summary>

        public static void Initialize()

        {

            Mapper.Initialize(config =>

            {

                Profiles.ToList().ForEach(file =>

                {

                    config.AddProfile(file);

                });

            });

        }

    }

其他地方需要用mapper的地方 调用方式:

AutoMapperManager.AddProfile(new Profile1());

AutoMapperManager.AddProfile(new Profile2());

AutoMapperManager.Initialize();

参考:

https://www.cnblogs.com/jobs2/p/3503990.html

https://www.cnblogs.com/youring2/p/automapper.html

http://www.cnblogs.com/dudu/p/5875579.html

【.NET】AutoMapper学习记录的更多相关文章

  1. Quartz 学习记录1

    原因 公司有一些批量定时任务可能需要在夜间执行,用的是quartz和spring batch两个框架.quartz是个定时任务框架,spring batch是个批处理框架. 虽然我自己的小玩意儿平时不 ...

  2. Java 静态内部类与非静态内部类 学习记录.

    目的 为什么会有这篇文章呢,是因为我在学习各种框架的时候发现很多框架都用到了这些内部类的小技巧,虽然我平时写代码的时候基本不用,但是看别人代码的话至少要了解基本知识吧,另外到底内部类应该应用在哪些场合 ...

  3. Apache Shiro 学习记录4

    今天看了教程的第三章...是关于授权的......和以前一样.....自己也研究了下....我觉得看那篇教程怎么说呢.....总体上是为数不多的精品教程了吧....但是有些地方确实是讲的太少了.... ...

  4. UWP学习记录12-应用到应用的通信

    UWP学习记录12-应用到应用的通信 1.应用间通信 “共享”合约是用户可以在应用之间快速交换数据的一种方式. 例如,用户可能希望使用社交网络应用与其好友共享网页,或者将链接保存在笔记应用中以供日后参 ...

  5. UWP学习记录11-设计和UI

    UWP学习记录11-设计和UI 1.输入和设备 通用 Windows 平台 (UWP) 中的用户交互组合了输入和输出源(例如鼠标.键盘.笔.触摸.触摸板.语音.Cortana.控制器.手势.注视等)以 ...

  6. UWP学习记录10-设计和UI之控件和模式7

    UWP学习记录10-设计和UI之控件和模式7 1.导航控件 Hub,中心控件,利用它你可以将应用内容整理到不同但又相关的区域或类别中. 中心的各个区域可按首选顺序遍历,并且可用作更具体体验的起始点. ...

  7. UWP学习记录9-设计和UI之控件和模式6

    UWP学习记录9-设计和UI之控件和模式6 1.图形和墨迹 InkCanvas是接收和显示墨迹笔划的控件,是新增的比较复杂的控件,这里先不深入. 而形状(Shape)则是可以显示的各种保留模式图形对象 ...

  8. UWP学习记录8-设计和UI之控件和模式5

    UWP学习记录8-设计和UI之控件和模式5 1.日历.日期和时间控件 日期和时间控件提供了标准的本地化方法,可供用户在应用中查看并设置日期和时间值. 有四个日期和时间控件可供选择,选择的依据如下: 日 ...

  9. UWP学习记录7-设计和UI之控件和模式4

    UWP学习记录7-设计和UI之控件和模式4 1.翻转视图 使用翻转视图浏览集合中的图像或其他项目(例如相册中的照片或产品详细信息页中的项目),一次显示一个项目. 对于触摸设备,轻扫某个项将在整个集合中 ...

随机推荐

  1. Java Int和Integer有什么区别?

    Int int是我们常说的整型数字,是Java的8个原始数据类型(Primitive Type:boolean.byte.short.char.int.float.double.long)之一.Jav ...

  2. Tomcat java.lang.OutOfMemoryError: PermGen space error

    Often time, Tomcat may hits the following java.lang.OutOfMemoryError: PermGen space error. java.lang ...

  3. [one day one question] iphone6 plus h5页面滑动莫名卡

    问题描述: iphone6 plus h5页面滑动莫名卡,这怎么破? 解决方案: 比较奇葩的问题,在找不到任何问题的情况下,可以考虑在下发现的解决方案,html,body未添加height: 100% ...

  4. 微信小程序:WXSS 样式

    微信小程序:WXSS 样式 一.WXSS 样式 XSS 具有 CSS 大部分的特性,小程序在 WXSS 也做了一些扩充和修改. 1,新增了尺寸单位: 在写 CSS 样式时,开发者需要考虑到手机设备的屏 ...

  5. Python3.x:基础学习

    Python3.x:基础学习 1,Python有五种标准数据类型 1.数字 2.字符串 3.列表 4.元组 5.字典 (1).数字 数字数据类型存储数字值.当为其分配值时,将创建数字对象. var1 ...

  6. hyper-v安装虚拟机ubuntu 18.04 64bit后无法使能增强模式怎么办

    1.获取脚本来使能增强模式 $ sudo apt-get update $ sudo apt install git $ git clone https://github.com/jterry75/x ...

  7. sql批处理(batch)的简单使用

    批处理指的是一次操作中执行多条SQL语句,相比于一次一次执行效率会提高很多 批处理主要是分两步: 将要执行的SQL语句保存 执行SQL语句 Statement和PreparedStatement都支持 ...

  8. A TLS packet with unexpected length was received 解决方法

    参考:A TLS packet with unexpected length was received. 系统环境 主系统 OS X,虚拟机 Ubuntu 14.04 64bit. 问题描述 在git ...

  9. python StringIO类

    python的stringIO类用来处理字符串,由于其操作类似文件操作,可以视为内存中的文件. 1.创建stringIO 2.常用操作: write,writelines.getvalue.seek. ...

  10. POJ 3613 Cow Relays(floyd+快速幂)

    http://poj.org/problem?id=3613 题意: 求经过k条路径的最短路径. 思路: 如果看过<矩阵乘法在信息学的应用>这篇论文就会知道 现在我们在邻接矩阵中保存距离, ...