当前位置: 首页 > news >正文

【.NET】AutoMapper学习记录

在两个不同的类型对象之间传输数据,通常我们会用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(),

            });

        }

}
View Code

【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));

        }
View 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);

                });

            });

        }

    }
View Code

其他地方需要用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

转载于:https://www.cnblogs.com/peterYong/p/10097178.html

相关文章:

  • Javassm连接数据库报错129 ERROR [com.alibaba.druid.pool.DruidDataSource] - {dataSource-1} init error...
  • 从AI医疗到量子计算,亚洲研究院如何成为微软发展的生命力?
  • java B2B2C Springboot仿淘宝电子商城系统--Spring Cloud Gateway
  • Rust 1.31正式发布,首次引入Rust 2018新功能
  • Centos7Yum安装配置指定版本nginx
  • SPRING 集成 activemq 的 topic 模式
  • Confluence 6 对一个空间进行归档后产生的影响
  • Docker之 数据持久化
  • webpack4 配置 vue项目
  • 开发函数计算的正确姿势 —— 排查超时问题
  • 同是容器管理系统,Kubernetes为什么那么火?
  • Beta冲刺随笔集
  • 计算机系统要素-写一个简单的编译器和操作系统
  • SQLite教程
  • webpack入门学习手记(二)
  • 0基础学习移动端适配
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • Codepen 每日精选(2018-3-25)
  • Django 博客开发教程 16 - 统计文章阅读量
  • ES6简单总结(搭配简单的讲解和小案例)
  • express.js的介绍及使用
  • laravel 用artisan创建自己的模板
  • Node项目之评分系统(二)- 数据库设计
  • Redis 懒删除(lazy free)简史
  • Ruby 2.x 源代码分析:扩展 概述
  • SOFAMosn配置模型
  • Synchronized 关键字使用、底层原理、JDK1.6 之后的底层优化以及 和ReenTrantLock 的对比...
  • VUE es6技巧写法(持续更新中~~~)
  • 免费小说阅读小程序
  • 三栏布局总结
  • 使用iElevator.js模拟segmentfault的文章标题导航
  • 验证码识别技术——15分钟带你突破各种复杂不定长验证码
  • 怎么把视频里的音乐提取出来
  • 新海诚画集[秒速5センチメートル:樱花抄·春]
  • ​HTTP与HTTPS:网络通信的安全卫士
  • ​LeetCode解法汇总2670. 找出不同元素数目差数组
  • #git 撤消对文件的更改
  • #laravel 通过手动安装依赖PHPExcel#
  • #LLM入门|Prompt#2.3_对查询任务进行分类|意图分析_Classification
  • #我与Java虚拟机的故事#连载08:书读百遍其义自见
  • (07)Hive——窗口函数详解
  • (Bean工厂的后处理器入门)学习Spring的第七天
  • (八)光盘的挂载与解挂、挂载CentOS镜像、rpm安装软件详细学习笔记
  • (附表设计)不是我吹!超级全面的权限系统设计方案面世了
  • (附源码)springboot车辆管理系统 毕业设计 031034
  • (学习日记)2024.04.10:UCOSIII第三十八节:事件实验
  • (原创)Stanford Machine Learning (by Andrew NG) --- (week 9) Anomaly DetectionRecommender Systems...
  • (转)C#开发微信门户及应用(1)--开始使用微信接口
  • (转)关于pipe()的详细解析
  • .Family_物联网
  • .gitattributes 文件
  • .gitignore文件设置了忽略但不生效
  • .net framework profiles /.net framework 配置
  • .net mvc部分视图
  • .NET/C# 阻止屏幕关闭,阻止系统进入睡眠状态