在两个不同的类型对象之间传输数据,通常我们会用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. 手把手教你学node.js之一个简单的express应用

    一个简单的express应用 目标 建立一个 lesson1 项目,在其中编写代码.当在浏览器中访问 http://localhost:3000/ 时,输出 Hello World. 挑战 访问 ht ...

  2. samba创建权限(复制)

    1.今天发现用samba挂载开发过程中,发现别人的权限和我的经常不一致,导致提交git的时候都被提交了,心想应该有哪里有一个统一设置的可以设置用户的默认权限,后来百度之后找到下面的文章,以后创建文件和 ...

  3. 11月16host文件

    #################################################################################################### ...

  4. python使用set来去重碰到TypeError: unhashable type

    新版:Python 的 unhashable type 错误分析及解决 python使用set来去重是一种常用的方法. 一般使用方法如下: # int a = [1, 2, 3, 4, 5, 1, 2 ...

  5. 20145315 《Java程序设计》第七周学习总结

    20145315 <Java程序设计>第七周学习总结 教材学习内容总结 第十三章 时间与日期 13.1.1时间的度量 1.格林威治时间(GMT):参考太阳到达最高点,有时间误差. 2.世界 ...

  6. Ubuntu16.04 远程访问RabbitMQ

    我们在虚拟机里面安装好RabbitMQ以后,虽然可以在虚拟机中访问,但是在主机端并不能访问 现在要解决这个问题 第一:账户 RabbitMQ为了安全性考虑,默认的guest账户只能在本地127.0.0 ...

  7. LA 7278 Game of Cards(SG函数)

    https://vjudge.net/problem/UVALive-7278 题意: 两个人玩游戏,现在有n堆牌,轮到自己时,先在牌堆中选一堆牌,先在牌堆中选择拿走0~k张牌(至少得剩下一张),然后 ...

  8. python stat获取文件信息

    import os statinfo = os.stat('qqq.txt') print(statinfo,"\n") print(statinfo.st_mode) 输出 os ...

  9. python 使用两个列表合成字典

    keys = ['red', 'green', 'blue'] values = ['#FF0000','#008000', '#0000FF'] color_dictionary = dict(zi ...

  10. 定义 S4 类

    S3 类仅用一个字符向量表示,与之不同的是,S4 类要求对类和方法有正式定义.为了定义一个 S4 类,我们需要调用 setClass( ),并提供一种类成员的表示,这种表示被称为字段(slots).通 ...