在两个不同的类型对象之间传输数据,通常我们会用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. A Practical Guide to Support Vector Classi cation

    <A Practical Guide to Support Vector Classication>是一篇libSVM使用入门教程以及一些实用技巧. 1. Basic Kernels: ( ...

  2. HTTP从入门到入土(3)——TCP三次握手

    TCP三次握手 客户端与服务器之间互相发送HTTP请求响应之前需要先进行TCP连接,因为HTTP是一个无连接.无状态协议,不存在连接的概念,只有请求和响应的概念.而请求和响应实际上只是数据包,他们需要 ...

  3. Thinkphp在Lnmp环境下部署项目先后报错问题解决:_STORAGE_WRITE_ERROR_:./Application/Runtime/Cache/Home/...Access denied.

    首先报错:_STORAGE_WRITE_ERROR_:./Application/Runtime/Cache/Home/769e70f2e46f34ceb60619bbda5e4691.php 解决此 ...

  4. Python3.x:os.chdir(改变当前路径方法)介绍

    Python3.x:os.chdir(改变当前路径方法)介绍 1,os.chdir() import os os.chdir(r'C:\python36\test_chdir') 说明:chdir() ...

  5. linux第五周

    第五周 给MenuOS增加time和time-asm命令 更新menu代码到最新版 在main函数中增加MenuConfig 增加对应的Time函数和TimeAsm函数 make rootfs 使用g ...

  6. Block作为参数时的使用

    Block作为参数使用,常见于各框架之中,比如在封装一个类时,当做什么事情由外界去决定,什么时候调用由自己的类决定时,这时候就需要将block作为参数使用. 下面我们模仿AFNetworking的ma ...

  7. What's the difference between SDK and Runtime in .NET Core?

    What's the difference between SDK and Runtime in .NET Core? Answer1 According to the .Net Core Guide ...

  8. javascript method.

    //**************************************************************** //* 名 称:DataLength //* 功 能:计算数据的长 ...

  9. adb connect 192.168.1.10 failed to connect to 192.168.1.10:5555

    adb connect 192.168.1.10 输出 failed to connect to 关闭安卓端Wi-Fi,重新打开连接即可

  10. Decorator(装饰)

    意图: 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator 模式相比生成子类更为灵活. 适用性: 在不影响其他对象的情况下,以动态.透明的方式给单个对象添加职责. 处理那些可以撤消 ...